├── scripts ├── fingerprintjs │ ├── fingerprint.js │ ├── SOURCE │ └── LICENSE ├── long │ └── SOURCE ├── zlib │ ├── SOURCE │ ├── LICENSE │ └── inflate.min.js ├── jsgif │ ├── SOURCE │ └── LICENSE ├── fontjs │ ├── SOURCE │ └── LICENSE ├── jsBox2D │ ├── SOURCE │ └── LICENSE ├── spine │ └── SOURCE ├── particles │ ├── IDR_GIF1.png │ ├── IDR_GIF10.png │ ├── IDR_GIF11.png │ ├── IDR_GIF12.png │ ├── IDR_GIF13.png │ ├── IDR_GIF14.png │ ├── IDR_GIF15.png │ ├── IDR_GIF2.png │ ├── IDR_GIF3.png │ ├── IDR_GIF4.png │ ├── IDR_GIF5.png │ ├── IDR_GIF6.png │ ├── IDR_GIF7.png │ ├── IDR_GIF8.png │ └── IDR_GIF9.png ├── zeusparticles │ ├── IDR_GIF1.png │ ├── IDR_GIF10.png │ ├── IDR_GIF11.png │ ├── IDR_GIF12.png │ ├── IDR_GIF13.png │ ├── IDR_GIF14.png │ ├── IDR_GIF15.png │ ├── IDR_GIF2.png │ ├── IDR_GIF3.png │ ├── IDR_GIF4.png │ ├── IDR_GIF5.png │ ├── IDR_GIF6.png │ ├── IDR_GIF7.png │ ├── IDR_GIF8.png │ └── IDR_GIF9.png ├── builtinfonts │ └── FONT_builtin.png ├── Plane.js ├── yy3DModel.js ├── functions │ ├── Function_IAP.js │ ├── Function_Buffers.js │ ├── Function_Tiles.js │ ├── Function_Action.js │ ├── Function_Gamepad.js │ ├── Function_Misc.js │ ├── Function_Timeline.js │ └── Function_AnimCurve.js ├── yyTile.js ├── yyPlayfield.js ├── GameGlobals.js ├── animation │ └── yySkeletonSkin.js ├── sound │ ├── effects │ │ ├── Gain.js │ │ ├── LPF2.js │ │ ├── HPF2.js │ │ ├── PeakEQ.js │ │ ├── HiShelf.js │ │ ├── LoShelf.js │ │ ├── Reverb1.js │ │ ├── Delay.js │ │ ├── Tremolo.js │ │ ├── Bitcrusher.js │ │ ├── Compressor.js │ │ └── EQ.js │ ├── worklets │ │ ├── GainProcessor.js │ │ ├── EQProcessor.js │ │ ├── AudioBusProcessor.js │ │ ├── BitcrusherProcessor.js │ │ ├── DelayProcessor.js │ │ ├── LPF2Processor.js │ │ ├── HPF2Processor.js │ │ ├── TremoloProcessor.js │ │ ├── WavetableLFO.js │ │ ├── PeakEQProcessor.js │ │ ├── HiShelfProcessor.js │ │ ├── LoShelfProcessor.js │ │ └── CompressorProcessor.js │ ├── WorkletNodeManager.js │ ├── AudioPropsCalc.js │ ├── AudioEmitter.js │ ├── AudioPlaybackProps.js │ └── AudioBus.js ├── utils │ └── TimeRampedParamLinear.js ├── LocalStorage.js ├── Frustum.js ├── libWebGL │ ├── yySamplerState.js │ ├── shaders │ │ └── basicShader.js │ └── yyGLTexture.js ├── yyTrigger.js ├── yyQueue.js ├── Storage.js ├── yyWeakRef.js ├── physics │ └── yyPhysicsDebugRender.js ├── SWF │ └── yySWFTimeline.js └── yyAllocate.js ├── .gitignore ├── doc ├── set-html5-runner-path.png └── std-html5-runner-path.png ├── package.json ├── licence.txt ├── .eslintrc.js ├── .claude └── settings.json └── README.md /scripts/fingerprintjs/fingerprint.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .vscode 3 | *~ 4 | -------------------------------------------------------------------------------- /scripts/long/SOURCE: -------------------------------------------------------------------------------- 1 | https://github.com/dcodeIO/long.js 2 | -------------------------------------------------------------------------------- /scripts/zlib/SOURCE: -------------------------------------------------------------------------------- 1 | https://github.com/imaya/zlib.js 2 | -------------------------------------------------------------------------------- /scripts/jsgif/SOURCE: -------------------------------------------------------------------------------- 1 | https://github.com/antimatter15/jsgif 2 | -------------------------------------------------------------------------------- /scripts/fingerprintjs/SOURCE: -------------------------------------------------------------------------------- 1 | https://github.com/Valve/fingerprintjs 2 | -------------------------------------------------------------------------------- /scripts/fontjs/SOURCE: -------------------------------------------------------------------------------- 1 | https://github.com/Pomax/lib-font/tree/v2015 2 | -------------------------------------------------------------------------------- /scripts/jsBox2D/SOURCE: -------------------------------------------------------------------------------- 1 | https://code.google.com/archive/p/jsbox2d/ 2 | -------------------------------------------------------------------------------- /scripts/spine/SOURCE: -------------------------------------------------------------------------------- 1 | https://github.com/EsotericSoftware/spine-runtimes/tree/4.0/spine-ts 2 | -------------------------------------------------------------------------------- /doc/set-html5-runner-path.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YoYoGames/GameMaker-HTML5/HEAD/doc/set-html5-runner-path.png -------------------------------------------------------------------------------- /doc/std-html5-runner-path.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YoYoGames/GameMaker-HTML5/HEAD/doc/std-html5-runner-path.png -------------------------------------------------------------------------------- /scripts/particles/IDR_GIF1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YoYoGames/GameMaker-HTML5/HEAD/scripts/particles/IDR_GIF1.png -------------------------------------------------------------------------------- /scripts/particles/IDR_GIF10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YoYoGames/GameMaker-HTML5/HEAD/scripts/particles/IDR_GIF10.png -------------------------------------------------------------------------------- /scripts/particles/IDR_GIF11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YoYoGames/GameMaker-HTML5/HEAD/scripts/particles/IDR_GIF11.png -------------------------------------------------------------------------------- /scripts/particles/IDR_GIF12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YoYoGames/GameMaker-HTML5/HEAD/scripts/particles/IDR_GIF12.png -------------------------------------------------------------------------------- /scripts/particles/IDR_GIF13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YoYoGames/GameMaker-HTML5/HEAD/scripts/particles/IDR_GIF13.png -------------------------------------------------------------------------------- /scripts/particles/IDR_GIF14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YoYoGames/GameMaker-HTML5/HEAD/scripts/particles/IDR_GIF14.png -------------------------------------------------------------------------------- /scripts/particles/IDR_GIF15.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YoYoGames/GameMaker-HTML5/HEAD/scripts/particles/IDR_GIF15.png -------------------------------------------------------------------------------- /scripts/particles/IDR_GIF2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YoYoGames/GameMaker-HTML5/HEAD/scripts/particles/IDR_GIF2.png -------------------------------------------------------------------------------- /scripts/particles/IDR_GIF3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YoYoGames/GameMaker-HTML5/HEAD/scripts/particles/IDR_GIF3.png -------------------------------------------------------------------------------- /scripts/particles/IDR_GIF4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YoYoGames/GameMaker-HTML5/HEAD/scripts/particles/IDR_GIF4.png -------------------------------------------------------------------------------- /scripts/particles/IDR_GIF5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YoYoGames/GameMaker-HTML5/HEAD/scripts/particles/IDR_GIF5.png -------------------------------------------------------------------------------- /scripts/particles/IDR_GIF6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YoYoGames/GameMaker-HTML5/HEAD/scripts/particles/IDR_GIF6.png -------------------------------------------------------------------------------- /scripts/particles/IDR_GIF7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YoYoGames/GameMaker-HTML5/HEAD/scripts/particles/IDR_GIF7.png -------------------------------------------------------------------------------- /scripts/particles/IDR_GIF8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YoYoGames/GameMaker-HTML5/HEAD/scripts/particles/IDR_GIF8.png -------------------------------------------------------------------------------- /scripts/particles/IDR_GIF9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YoYoGames/GameMaker-HTML5/HEAD/scripts/particles/IDR_GIF9.png -------------------------------------------------------------------------------- /scripts/zeusparticles/IDR_GIF1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YoYoGames/GameMaker-HTML5/HEAD/scripts/zeusparticles/IDR_GIF1.png -------------------------------------------------------------------------------- /scripts/zeusparticles/IDR_GIF10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YoYoGames/GameMaker-HTML5/HEAD/scripts/zeusparticles/IDR_GIF10.png -------------------------------------------------------------------------------- /scripts/zeusparticles/IDR_GIF11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YoYoGames/GameMaker-HTML5/HEAD/scripts/zeusparticles/IDR_GIF11.png -------------------------------------------------------------------------------- /scripts/zeusparticles/IDR_GIF12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YoYoGames/GameMaker-HTML5/HEAD/scripts/zeusparticles/IDR_GIF12.png -------------------------------------------------------------------------------- /scripts/zeusparticles/IDR_GIF13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YoYoGames/GameMaker-HTML5/HEAD/scripts/zeusparticles/IDR_GIF13.png -------------------------------------------------------------------------------- /scripts/zeusparticles/IDR_GIF14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YoYoGames/GameMaker-HTML5/HEAD/scripts/zeusparticles/IDR_GIF14.png -------------------------------------------------------------------------------- /scripts/zeusparticles/IDR_GIF15.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YoYoGames/GameMaker-HTML5/HEAD/scripts/zeusparticles/IDR_GIF15.png -------------------------------------------------------------------------------- /scripts/zeusparticles/IDR_GIF2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YoYoGames/GameMaker-HTML5/HEAD/scripts/zeusparticles/IDR_GIF2.png -------------------------------------------------------------------------------- /scripts/zeusparticles/IDR_GIF3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YoYoGames/GameMaker-HTML5/HEAD/scripts/zeusparticles/IDR_GIF3.png -------------------------------------------------------------------------------- /scripts/zeusparticles/IDR_GIF4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YoYoGames/GameMaker-HTML5/HEAD/scripts/zeusparticles/IDR_GIF4.png -------------------------------------------------------------------------------- /scripts/zeusparticles/IDR_GIF5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YoYoGames/GameMaker-HTML5/HEAD/scripts/zeusparticles/IDR_GIF5.png -------------------------------------------------------------------------------- /scripts/zeusparticles/IDR_GIF6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YoYoGames/GameMaker-HTML5/HEAD/scripts/zeusparticles/IDR_GIF6.png -------------------------------------------------------------------------------- /scripts/zeusparticles/IDR_GIF7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YoYoGames/GameMaker-HTML5/HEAD/scripts/zeusparticles/IDR_GIF7.png -------------------------------------------------------------------------------- /scripts/zeusparticles/IDR_GIF8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YoYoGames/GameMaker-HTML5/HEAD/scripts/zeusparticles/IDR_GIF8.png -------------------------------------------------------------------------------- /scripts/zeusparticles/IDR_GIF9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YoYoGames/GameMaker-HTML5/HEAD/scripts/zeusparticles/IDR_GIF9.png -------------------------------------------------------------------------------- /scripts/builtinfonts/FONT_builtin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YoYoGames/GameMaker-HTML5/HEAD/scripts/builtinfonts/FONT_builtin.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "scripts", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "runner.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "Apache-2.0", 12 | "devDependencies": { 13 | "eslint": "^6.3.0" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /scripts/Plane.js: -------------------------------------------------------------------------------- 1 | function Plane() 2 | { 3 | this.Normal = new Vector3(0.0, 0.0, 1.0); 4 | 5 | this.Distance = 0.0; 6 | } 7 | 8 | Plane.prototype.Normalise = function () 9 | { 10 | var length = this.Normal.Length(); 11 | this.Normal.X /= length; 12 | this.Normal.Y /= length; 13 | this.Normal.Z /= length; 14 | this.Distance /= length; 15 | }; 16 | -------------------------------------------------------------------------------- /scripts/yy3DModel.js: -------------------------------------------------------------------------------- 1 | // ********************************************************************************************************************** 2 | // 3 | // Copyright (c)2011, YoYo Games Ltd. All Rights reserved. 4 | // 5 | // File: yy3DModel.js 6 | // Created: 16/02/2011 7 | // Author: Mike 8 | // Project: HTML5 9 | // Description: 10 | // 11 | // ********************************************************************************************************************** 12 | -------------------------------------------------------------------------------- /scripts/functions/Function_IAP.js: -------------------------------------------------------------------------------- 1 | // ********************************************************************************************************************** 2 | // 3 | // Copyright (c)2011, YoYo Games Ltd. All Rights reserved. 4 | // 5 | // File: Function_IAP.js 6 | // Created: 16/02/2011 7 | // Author: Mike 8 | // Project: HTML5 9 | // Description: 10 | // 11 | // ********************************************************************************************************************** 12 | 13 | -------------------------------------------------------------------------------- /licence.txt: -------------------------------------------------------------------------------- 1 | Copyright 2021-2022, YoYo Games Ltd. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at: 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. -------------------------------------------------------------------------------- /scripts/yyTile.js: -------------------------------------------------------------------------------- 1 | 2 | // ********************************************************************************************************************** 3 | // 4 | // Copyright (c)2011, YoYo Games Ltd. All Rights reserved. 5 | // 6 | // File: yyTiles.js 7 | // Created: 17/02/2011 8 | // Author: Mike 9 | // Project: HTML5 10 | // Description: 11 | // 12 | // Date Version BY Comment 13 | // ---------------------------------------------------------------------------------------------------------------------- 14 | // 17/02/2011 15 | // 16 | // ********************************************************************************************************************** 17 | -------------------------------------------------------------------------------- /scripts/functions/Function_Buffers.js: -------------------------------------------------------------------------------- 1 | // ********************************************************************************************************************** 2 | // 3 | // Copyright (c)2013, YoYo Games Ltd. All Rights reserved. 4 | // 5 | // File: Function_Buffers.js 6 | // Created: 27/02/2013 7 | // Author: Mike 8 | // Project: HTML5 9 | // Description: Blocking in buffer functions 10 | // 11 | // Date Version BY Comment 12 | // ---------------------------------------------------------------------------------------------------------------------- 13 | // 27/02/2013 14 | // 15 | // ********************************************************************************************************************** 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /scripts/functions/Function_Tiles.js: -------------------------------------------------------------------------------- 1 | 2 | // ********************************************************************************************************************** 3 | // 4 | // Copyright (c)2011, YoYo Games Ltd. All Rights reserved. 5 | // 6 | // File: Function_Tiles.js 7 | // Created: 17/05/2011 8 | // Author: Mike 9 | // Project: HTML5 10 | // Description: 11 | // 12 | // Date Version BY Comment 13 | // ---------------------------------------------------------------------------------------------------------------------- 14 | // 17/05/2011 V1.0 MJD Stub functions added 15 | // 16 | // ********************************************************************************************************************** 17 | -------------------------------------------------------------------------------- /scripts/yyPlayfield.js: -------------------------------------------------------------------------------- 1 | 2 | // ********************************************************************************************************************** 3 | // 4 | // Copyright (c)2011, YoYo Games Ltd. All Rights reserved. 5 | // 6 | // File: yyPlayfield.js 7 | // Created: 17/02/2011 8 | // Author: Mike 9 | // Project: HTML5 10 | // Description: Playfields hold lists of tiles, batched by depth 11 | // 12 | // Date Version BY Comment 13 | // ---------------------------------------------------------------------------------------------------------------------- 14 | // 17/05/2011 V1.0 MJD 1st version 15 | // 16 | // ********************************************************************************************************************** 17 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "env": { 3 | "browser": true, 4 | "es6": false 5 | }, 6 | "extends": "eslint:recommended", 7 | "globals": { 8 | "Atomics": "readonly", 9 | "SharedArrayBuffer": "readonly" 10 | }, 11 | "parserOptions": { 12 | "ecmaVersion": 2018, 13 | "sourceType": "module" 14 | }, 15 | "rules": { 16 | "semi": ["error", "always"], 17 | "no-undef" : 1, 18 | 'no-unused-vars': 1, 19 | 'no-prototype-builtins': 0, 20 | 'no-mixed-spaces-and-tabs': 0, 21 | 'no-extra-semi': 1, 22 | 'no-fallthrough': 1, 23 | 'no-redeclare': 1, 24 | 'no-empty': 1, 25 | 'no-constant-condition': 1, 26 | 'no-inner-declarations': 0, 27 | 'no-func-assign': 0, 28 | 'no-cond-assign': 0 29 | } 30 | }; -------------------------------------------------------------------------------- /scripts/functions/Function_Action.js: -------------------------------------------------------------------------------- 1 | 2 | // ********************************************************************************************************************** 3 | // 4 | // Copyright (c)2011, YoYo Games Ltd. All Rights reserved. 5 | // 6 | // File: Function_Action.js 7 | // Created: 26/05/2011 8 | // Author: Mike 9 | // Project: HTML5 10 | // Description: 11 | // 12 | // Date Version BY Comment 13 | // ---------------------------------------------------------------------------------------------------------------------- 14 | // 22/02/2011 V1.0 MJD 1st version, blocked in.... 15 | // 16 | // ********************************************************************************************************************** 17 | 18 | // Whether the arguments to a function are relative 19 | var Argument_Relative = false; 20 | -------------------------------------------------------------------------------- /scripts/GameGlobals.js: -------------------------------------------------------------------------------- 1 | 2 | // ********************************************************************************************************************** 3 | // 4 | // Copyright (c)2011, YoYo Games Ltd. All Rights reserved. 5 | // 6 | // File: global.js 7 | // Created: 20/02/2011 8 | // Author: Mike 9 | // Project: HTML5 10 | // Description: variables in "GameGlobals" (rather than yyGlobals), are GML globals, not engine globals. 11 | // 12 | // Date Version BY Comment 13 | // ---------------------------------------------------------------------------------------------------------------------- 14 | // 20/02/2011 V1.0 MJD 1st version 15 | // 16 | // ********************************************************************************************************************** 17 | 18 | 19 | function yyGameGlobals() 20 | { 21 | this.__type = "global"; 22 | } 23 | -------------------------------------------------------------------------------- /scripts/animation/yySkeletonSkin.js: -------------------------------------------------------------------------------- 1 | // ********************************************************************************************************************** 2 | // 3 | // Copyright (c)2011, YoYo Games Ltd. All Rights reserved. 4 | // 5 | // File: yySkeletonSkin.js 6 | // Created: 16/02/2011 7 | // Author: Mike 8 | // Project: HTML5 9 | // Description: 10 | // 11 | // ********************************************************************************************************************** 12 | 13 | // ############################################################################################# 14 | /// Function: 15 | /// Create a new SkeletonSkin object 16 | /// 17 | // ############################################################################################# 18 | // @if feature("spine") 19 | /** @constructor */ 20 | function yySkeletonSkin(_skin) 21 | { 22 | this.__type = "[SkeletonSkin]"; 23 | this.m_skin = _skin; 24 | } 25 | // @endif 26 | -------------------------------------------------------------------------------- /.claude/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "permissions": { 3 | "deny": [ 4 | "Read(**/.env*)", 5 | "Read(**/*.pem)", 6 | "Read(**/*.key)", 7 | "Read(**/.ssh/**)", 8 | "Read(**/.gcp*.json)", 9 | "Read(**/oauth2/**)", 10 | "Read(**/freedom/**)", 11 | "Read(**/stats/**)", 12 | "Read(**/auth/**)", 13 | "Read(**/avro/**)", 14 | "Read(**/crypto/**)", 15 | "Read(**/flow/**)", 16 | "Read(**/*.p12)", 17 | "Read(**/*.pfx)", 18 | "Read(**/.aws/**)", 19 | "Read(**/.azure/**)", 20 | "Read(**/*.crt)", 21 | "Read(**/*.csr)", 22 | "Read(**/.config/gcloud/**)", 23 | "Read(**/secrets/**)", 24 | "Read(**/.kube/config)", 25 | "Read(**/.docker/config.json)", 26 | "Read(**/*.pkcs*)", 27 | "Read(**/.npmrc)", 28 | "Read(**/.pypirc)", 29 | "Read(**/*.token)", 30 | "Read(**/.credentials)", 31 | "Read(**/.password*)", 32 | "Read(**/*.secret*)", 33 | "Read(**/.git-credentials)", 34 | "Read(**/.netrc)" 35 | ] 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /scripts/fingerprintjs/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 Valentin Vasilyev 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /scripts/fontjs/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 Mike "Pomax" Kamermans 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /scripts/jsBox2D/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Erin Catto 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /scripts/jsgif/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010-2014 Kevin Kwok 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /scripts/sound/effects/Gain.js: -------------------------------------------------------------------------------- 1 | // @if feature("audio_effects") 2 | function GainEffectStruct(_params) { 3 | AudioEffectStruct.call(this, AudioEffect.Type.Gain); 4 | Object.setPrototypeOf(this, AudioEffectStruct.prototype); 5 | 6 | this.initParams(_params); 7 | 8 | // Define user-facing properties 9 | Object.defineProperties(this, { 10 | gmlgain: { 11 | enumerable: true, 12 | get: () => { 13 | return this.params[GainEffectStruct.Index.Gain]; 14 | }, 15 | set: (_gain) => { 16 | const val = this.setParam(GainEffectStruct.Index.Gain, _gain); 17 | 18 | this.nodes.forEach((_node) => { 19 | const gain = _node.parameters.get("gain"); 20 | gain.value = val; 21 | }); 22 | } 23 | } 24 | }); 25 | } 26 | 27 | GainEffectStruct.Index = { 28 | Bypass: 0, 29 | Gain: 1 30 | }; 31 | 32 | GainEffectStruct.ParamDescriptors = [ 33 | { name: "bypass", integer: true, defaultValue: 0, minValue: 0, maxValue: 1 }, 34 | { name: "gain", integer: false, defaultValue: 0.5, minValue: 0.0, maxValue: Number.MAX_VALUE } 35 | ]; 36 | // @endif 37 | -------------------------------------------------------------------------------- /scripts/zlib/LICENSE: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * zlib.js 4 | * JavaScript Zlib Library 5 | * https://github.com/imaya/zlib.js 6 | * 7 | * The MIT License 8 | * 9 | * Copyright (c) 2012 imaya 10 | * 11 | * Permission is hereby granted, free of charge, to any person obtaining a copy 12 | * of this software and associated documentation files (the "Software"), to deal 13 | * in the Software without restriction, including without limitation the rights 14 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | * copies of the Software, and to permit persons to whom the Software is 16 | * furnished to do so, subject to the following conditions: 17 | * 18 | * The above copyright notice and this permission notice shall be included in 19 | * all copies or substantial portions of the Software. 20 | * 21 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 27 | * THE SOFTWARE. 28 | */ 29 | -------------------------------------------------------------------------------- /scripts/utils/TimeRampedParamLinear.js: -------------------------------------------------------------------------------- 1 | // @if feature("audio") 2 | function TimeRampedParamLinear(_initialVal) { 3 | this.__val = _initialVal; 4 | this.__origin = _initialVal; 5 | this.__target = _initialVal; 6 | this.__diff = 0; 7 | this.__rampStart = 0; 8 | this.__rampEnd = 0; 9 | this.__rampScalar = 0; 10 | } 11 | 12 | TimeRampedParamLinear.prototype.get = function() { 13 | return this.__val; 14 | }; 15 | 16 | TimeRampedParamLinear.prototype.set = function(_target, _timeMs = 0) { 17 | _timeMs = Math.max(0, _timeMs); 18 | 19 | if (_timeMs == 0) { 20 | this.__val = _target; 21 | this.__target = _target; 22 | this.__rampEnd = performance.now(); 23 | } 24 | else { 25 | this.__origin = this.__val; 26 | this.__target = _target; 27 | this.__diff = this.__target - this.__origin; 28 | 29 | this.__rampStart = performance.now(); 30 | this.__rampEnd = this.__rampStart + _timeMs; 31 | this.__rampScalar = 1 / (this.__rampEnd - this.__rampStart); 32 | } 33 | }; 34 | 35 | TimeRampedParamLinear.prototype.update = function() { 36 | const currentTime = performance.now(); 37 | 38 | if (currentTime >= this.__rampEnd) { 39 | this.__val = this.__target; 40 | } 41 | else { 42 | let frac = (currentTime - this.__rampStart) * this.__rampScalar; 43 | frac = Math.max(0, Math.min(frac, 1)); 44 | 45 | this.__val = this.__origin + (this.__diff * frac); 46 | } 47 | 48 | return this.__val; 49 | }; 50 | // @endif 51 | -------------------------------------------------------------------------------- /scripts/sound/worklets/GainProcessor.js: -------------------------------------------------------------------------------- 1 | class GainProcessor extends AudioWorkletProcessor 2 | { 3 | static get parameterDescriptors() 4 | { 5 | return [ 6 | { name: "bypass", automationRate: "a-rate", defaultValue: 0, minValue: 0, maxValue: 1 }, 7 | { name: "gain", automationRate: "a-rate", defaultValue: 0.5, minValue: 0.0 } 8 | ]; 9 | } 10 | 11 | constructor() 12 | { 13 | super(); 14 | this.makeMortal(); 15 | } 16 | 17 | process(inputs, outputs, parameters) 18 | { 19 | const input = inputs[0]; 20 | const output = outputs[0]; 21 | 22 | const bypass = parameters.bypass; 23 | const gain = parameters.gain; 24 | 25 | for (let c = 0; c < input.length; ++c) { 26 | const inputChannel = input[c]; 27 | const outputChannel = output[c]; 28 | 29 | for (let s = 0; s < inputChannel.length; ++s) { 30 | // Copy the input to the output 31 | outputChannel[s] = inputChannel[s]; 32 | 33 | // Check bypass state 34 | const b = (bypass[s] !== undefined) ? bypass[s] : bypass[0]; 35 | 36 | if (b > 0.0) { 37 | continue; 38 | } 39 | 40 | // Apply gain 41 | const g = (gain[s] !== undefined) ? gain[s] : gain[0]; 42 | 43 | outputChannel[s] *= g; 44 | } 45 | } 46 | 47 | return this.keepAlive; 48 | } 49 | } 50 | 51 | registerProcessor("gain-processor", GainProcessor); 52 | -------------------------------------------------------------------------------- /scripts/sound/effects/LPF2.js: -------------------------------------------------------------------------------- 1 | // @if feature("audio_effects") 2 | function LPF2EffectStruct(_params) { 3 | AudioEffectStruct.call(this, AudioEffect.Type.LPF2); 4 | Object.setPrototypeOf(this, AudioEffectStruct.prototype); 5 | 6 | this.initParams(_params); 7 | 8 | // Define user-facing properties 9 | Object.defineProperties(this, { 10 | gmlcutoff: { 11 | enumerable: true, 12 | get: () => { 13 | return this.params[LPF2EffectStruct.Index.Cutoff]; 14 | }, 15 | set: (_cutoff) => { 16 | const val = this.setParam(LPF2EffectStruct.Index.Cutoff, _cutoff); 17 | 18 | this.nodes.forEach((_node) => { 19 | const cutoff = _node.parameters.get("cutoff"); 20 | cutoff.value = val; 21 | }); 22 | } 23 | }, 24 | gmlq: { 25 | enumerable: true, 26 | get: () => { 27 | return this.params[LPF2EffectStruct.Index.Q]; 28 | }, 29 | set: (_q) => { 30 | const val = this.setParam(LPF2EffectStruct.Index.Q, _q); 31 | 32 | this.nodes.forEach((_node) => { 33 | const q = _node.parameters.get("q"); 34 | q.value = val; 35 | }); 36 | } 37 | } 38 | }); 39 | } 40 | 41 | LPF2EffectStruct.Index = { 42 | Bypass: 0, 43 | Cutoff: 1, 44 | Q: 2 45 | }; 46 | 47 | LPF2EffectStruct.ParamDescriptors = [ 48 | { name: "bypass", integer: true, defaultValue: 0, minValue: 0, maxValue: 1 }, 49 | { name: "cutoff", integer: false, defaultValue: 500.0, minValue: 10.0, maxValue: 20000.0 }, 50 | { name: "q", integer: false, defaultValue: 1.5, minValue: 1.0, maxValue: 100.0 } 51 | ]; 52 | // @endif 53 | -------------------------------------------------------------------------------- /scripts/sound/effects/HPF2.js: -------------------------------------------------------------------------------- 1 | // @if feature("audio_effects") 2 | function HPF2EffectStruct(_params) { 3 | AudioEffectStruct.call(this, AudioEffect.Type.HPF2); 4 | Object.setPrototypeOf(this, AudioEffectStruct.prototype); 5 | 6 | this.initParams(_params); 7 | 8 | // Define user-facing properties 9 | Object.defineProperties(this, { 10 | gmlcutoff: { 11 | enumerable: true, 12 | get: () => { 13 | return this.params[HPF2EffectStruct.Index.Cutoff]; 14 | }, 15 | set: (_cutoff) => { 16 | const val = this.setParam(HPF2EffectStruct.Index.Cutoff, _cutoff); 17 | 18 | this.nodes.forEach((_node) => { 19 | const cutoff = _node.parameters.get("cutoff"); 20 | cutoff.value = val; 21 | }); 22 | } 23 | }, 24 | gmlq: { 25 | enumerable: true, 26 | get: () => { 27 | return this.params[HPF2EffectStruct.Index.Q]; 28 | }, 29 | set: (_q) => { 30 | const val = this.setParam(HPF2EffectStruct.Index.Q, _q); 31 | 32 | this.nodes.forEach((_node) => { 33 | const q = _node.parameters.get("q"); 34 | q.value = val; 35 | }); 36 | } 37 | } 38 | }); 39 | } 40 | 41 | HPF2EffectStruct.Index = { 42 | Bypass: 0, 43 | Cutoff: 1, 44 | Q: 2 45 | }; 46 | 47 | HPF2EffectStruct.ParamDescriptors = [ 48 | { name: "bypass", integer: true, defaultValue: 0, minValue: 0, maxValue: 1 }, 49 | { name: "cutoff", integer: false, defaultValue: 1500.0, minValue: 10.0, maxValue: 20000.0 }, 50 | { name: "q", integer: false, defaultValue: 1.5, minValue: 1.0, maxValue: 100.0 } 51 | ]; 52 | // @endif 53 | -------------------------------------------------------------------------------- /scripts/LocalStorage.js: -------------------------------------------------------------------------------- 1 | // ********************************************************************************************************************** 2 | // 3 | // Copyright (c)2011, YoYo Games Ltd. All Rights reserved. 4 | // 5 | // File: LocalStorage.js 6 | // Created: 16/02/2011 7 | // Author: Mike 8 | // Project: HTML5 9 | // Description: 10 | // 11 | // ********************************************************************************************************************** 12 | 13 | // ############################################################################################# 14 | /// Function: 15 | /// 16 | // ############################################################################################# 17 | function SupportsLocalStorage() 18 | { 19 | try { 20 | if (('localStorage' in window) && (window['localStorage'] !== null)) { 21 | g_SupportsLocalStorage = true; 22 | } 23 | } 24 | catch (e) { 25 | g_SupportsLocalStorage = false; 26 | } 27 | return g_SupportsLocalStorage; 28 | } 29 | 30 | // ############################################################################################# 31 | /// Function: 32 | /// Builds the root key we use for generating localStorage ids 33 | /// 34 | // ############################################################################################# 35 | function GetLocalStorageRoot() 36 | { 37 | // Only use alpha-numeric characters, and underbars, from the display name 38 | let _displayName = (g_pGMFile.Options.DisplayName || "").replace(new RegExp("[^\\w]", "g"), ""); 39 | return (_displayName || "GameMaker") + "." + g_pBuiltIn.game_id + "."; 40 | } 41 | 42 | // ############################################################################################# 43 | /// Function: 44 | /// Returns the key used for the key-value pair when something is in local storage 45 | /// 46 | // ############################################################################################# 47 | function GetLocalStorageName(_fname) 48 | { 49 | return (g_pBuiltIn.local_storage + _fname); 50 | } 51 | -------------------------------------------------------------------------------- /scripts/sound/worklets/EQProcessor.js: -------------------------------------------------------------------------------- 1 | class EQInput extends AudioWorkletProcessor 2 | { 3 | static get parameterDescriptors() { 4 | return []; 5 | } 6 | 7 | constructor() { 8 | super(); 9 | this.makeMortal(); 10 | } 11 | 12 | process(_inputs, _outputs, _parameters) { 13 | const input = _inputs[0]; 14 | const output0 = _outputs[0]; 15 | const output1 = _outputs[1]; 16 | 17 | for (let c = 0; c < input.length; ++c) { 18 | const inputChannel = input[c]; 19 | const output0Channel = output0[c]; 20 | const output1Channel = output1[c]; 21 | 22 | for (let s = 0; s < inputChannel.length; ++s) { 23 | output0Channel[s] = inputChannel[s]; 24 | output1Channel[s] = inputChannel[s]; 25 | } 26 | } 27 | 28 | return this.keepAlive; 29 | } 30 | } 31 | 32 | class EQOutput extends AudioWorkletProcessor 33 | { 34 | static get parameterDescriptors() { 35 | return [ 36 | { name: "bypass", automationRate: "a-rate", defaultValue: 0, minValue: 0, maxValue: 1 } 37 | ]; 38 | } 39 | 40 | constructor() { 41 | super(); 42 | this.makeMortal(); 43 | } 44 | 45 | process(_inputs, _outputs, _parameters) { 46 | const input0 = _inputs[0]; 47 | const input1 = _inputs[1]; 48 | const output = _outputs[0]; 49 | 50 | const bypass = _parameters.bypass; 51 | 52 | for (let c = 0; c < input1.length; ++c) { 53 | const input0Channel = input0[c]; 54 | const input1Channel = input1[c]; 55 | const outputChannel = output[c]; 56 | 57 | for (let s = 0; s < input0Channel.length; ++s) { 58 | const b = (bypass[s] !== undefined) ? bypass[s] : bypass[0]; 59 | 60 | if (b > 0) { 61 | outputChannel[s] = input1Channel[s]; 62 | } 63 | else { 64 | outputChannel[s] = input0Channel[s]; 65 | } 66 | } 67 | } 68 | 69 | return this.keepAlive; 70 | } 71 | } 72 | 73 | registerProcessor("eq-input", EQInput); 74 | registerProcessor("eq-output", EQOutput); 75 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GameMaker HTML5 Runtime 2 | 3 | ## Overview 4 | 5 | This is the source for GameMaker's HTML5 runtime. You can clone it, experiment with it, use your modified version in your GameMaker installation, and contribute your own changes to the repository. 6 | 7 | The runtime will continue to receive updates and fixes from the GameMaker team. You’re encouraged to contribute by creating Pull Requests, which will be reviewed by our team. 8 | 9 | ## Using the Runtime 10 | 11 | In order to use this runtime, you should clone the repository as normal. Once you have the code checked out, you can tell GameMaker to use your cloned repository rather than its built-in copy of the runtime. 12 | 13 | You do this by changing *Path to HTML5 runner* under *Platform Settings > HTML5* on the *Preferences* screen, for example if you have the repository checked out to `C:\source\GameMaker-HTML5`: 14 | 15 | ![Screenshot of Preferences screen](doc/set-html5-runner-path.png) 16 | 17 | To revert back to the built-in runner, simply change the path back to `${html5_runner_path}`: 18 | 19 | ![Screenshot of Preferences screen](doc/std-html5-runner-path.png) 20 | 21 | ## Reporting Issues 22 | 23 | Report bugs with the HTML5 runtime on the repository's [Issues page](https://github.com/YoYoGames/GameMaker-HTML5/issues). Whenever possible, please include an exported GameMaker project (`*.yyz`) demonstrating the issue. 24 | 25 | Any new feature requests for GameMaker should still be submitted through the [Contact page](https://contact.gamemaker.io/), as these are not specific to the HTML5 runtime. 26 | 27 | ## Contributing 28 | 29 | After you’ve made your changes, run your game in GameMaker using the HTML5 target, which should use your modified runtime. Make use of the Debugger and browser tools to fix any issues that might crop up. 30 | 31 | Once you’re confident with your changes, push your changes to your forked repository, and create a Pull Request in the original repository (click on "compare across forks"). 32 | 33 | Whenever possible, please include an exported GameMaker project (`*.yyz`) with Pull Requests demonstrating your changes. 34 | 35 | You may be asked to make changes to your source if our team finds any issues, so your pull request may not be accepted in its initial form. 36 | 37 | We look forward to seeing your contributions! 38 | -------------------------------------------------------------------------------- /scripts/sound/effects/PeakEQ.js: -------------------------------------------------------------------------------- 1 | // @if feature("audio_effects") 2 | function PeakEQEffectStruct(_params) { 3 | AudioEffectStruct.call(this, AudioEffect.Type.PeakEQ); 4 | Object.setPrototypeOf(this, AudioEffectStruct.prototype); 5 | 6 | this.initParams(_params); 7 | 8 | // Define user-facing properties 9 | Object.defineProperties(this, { 10 | gmlfreq: { 11 | enumerable: true, 12 | get: () => { 13 | return this.params[PeakEQEffectStruct.Index.Freq]; 14 | }, 15 | set: (_freq) => { 16 | const val = this.setParam(PeakEQEffectStruct.Index.Freq, _freq); 17 | 18 | this.nodes.forEach((_node) => { 19 | const freq = _node.parameters.get("freq"); 20 | freq.value = val; 21 | }); 22 | } 23 | }, 24 | gmlq: { 25 | enumerable: true, 26 | get: () => { 27 | return this.params[PeakEQEffectStruct.Index.Q]; 28 | }, 29 | set: (_q) => { 30 | const val = this.setParam(PeakEQEffectStruct.Index.Q, _q); 31 | 32 | this.nodes.forEach((_node) => { 33 | const q = _node.parameters.get("q"); 34 | q.value = val; 35 | }); 36 | } 37 | }, 38 | gmlgain: { 39 | enumerable: true, 40 | get: () => { 41 | return this.params[PeakEQEffectStruct.Index.Gain]; 42 | }, 43 | set: (_gain) => { 44 | const val = this.setParam(PeakEQEffectStruct.Index.Gain, _gain); 45 | 46 | this.nodes.forEach((_node) => { 47 | const gain = _node.parameters.get("gain"); 48 | gain.value = val; 49 | }); 50 | } 51 | } 52 | }); 53 | } 54 | 55 | PeakEQEffectStruct.Index = { 56 | Bypass: 0, 57 | Freq: 1, 58 | Q: 2, 59 | Gain: 3 60 | }; 61 | 62 | PeakEQEffectStruct.ParamDescriptors = [ 63 | { name: "bypass", integer: true, defaultValue: 0, minValue: 0, maxValue: 1 }, 64 | { name: "freq", integer: false, defaultValue: 1500.0, minValue: 10.0, maxValue: 20000.0 }, 65 | { name: "q", integer: false, defaultValue: 1.0, minValue: 1.0, maxValue: 100.0 }, 66 | { name: "gain", integer: false, defaultValue: 1e-2, minValue: 1e-6, maxValue: Number.MAX_VALUE } 67 | ]; 68 | // @endif 69 | -------------------------------------------------------------------------------- /scripts/sound/effects/HiShelf.js: -------------------------------------------------------------------------------- 1 | // @if feature("audio_effects") 2 | function HiShelfEffectStruct(_params) { 3 | AudioEffectStruct.call(this, AudioEffect.Type.HiShelf); 4 | Object.setPrototypeOf(this, AudioEffectStruct.prototype); 5 | 6 | this.initParams(_params); 7 | 8 | // Define user-facing properties 9 | Object.defineProperties(this, { 10 | gmlfreq: { 11 | enumerable: true, 12 | get: () => { 13 | return this.params[HiShelfEffectStruct.Index.Freq]; 14 | }, 15 | set: (_freq) => { 16 | const val = this.setParam(HiShelfEffectStruct.Index.Freq, _freq); 17 | 18 | this.nodes.forEach((_node) => { 19 | const freq = _node.parameters.get("freq"); 20 | freq.value = val; 21 | }); 22 | } 23 | }, 24 | gmlq: { 25 | enumerable: true, 26 | get: () => { 27 | return this.params[HiShelfEffectStruct.Index.Q]; 28 | }, 29 | set: (_q) => { 30 | const val = this.setParam(HiShelfEffectStruct.Index.Q, _q); 31 | 32 | this.nodes.forEach((_node) => { 33 | const q = _node.parameters.get("q"); 34 | q.value = val; 35 | }); 36 | } 37 | }, 38 | gmlgain: { 39 | enumerable: true, 40 | get: () => { 41 | return this.params[HiShelfEffectStruct.Index.Gain]; 42 | }, 43 | set: (_gain) => { 44 | const val = this.setParam(HiShelfEffectStruct.Index.Gain, _gain); 45 | 46 | this.nodes.forEach((_node) => { 47 | const gain = _node.parameters.get("gain"); 48 | gain.value = val; 49 | }); 50 | } 51 | } 52 | }); 53 | } 54 | 55 | HiShelfEffectStruct.Index = { 56 | Bypass: 0, 57 | Freq: 1, 58 | Q: 2, 59 | Gain: 3 60 | }; 61 | 62 | HiShelfEffectStruct.ParamDescriptors = [ 63 | { name: "bypass", integer: true, defaultValue: 0, minValue: 0, maxValue: 1 }, 64 | { name: "freq", integer: false, defaultValue: 5000.0, minValue: 10.0, maxValue: 20000.0 }, 65 | { name: "q", integer: false, defaultValue: 1.0, minValue: 1.0, maxValue: 100.0 }, 66 | { name: "gain", integer: false, defaultValue: 1e-2, minValue: 1e-6, maxValue: Number.MAX_VALUE } 67 | ]; 68 | // @endif 69 | -------------------------------------------------------------------------------- /scripts/sound/effects/LoShelf.js: -------------------------------------------------------------------------------- 1 | // @if feature("audio_effects") 2 | function LoShelfEffectStruct(_params) { 3 | AudioEffectStruct.call(this, AudioEffect.Type.LoShelf); 4 | Object.setPrototypeOf(this, AudioEffectStruct.prototype); 5 | 6 | this.initParams(_params); 7 | 8 | // Define user-facing properties 9 | Object.defineProperties(this, { 10 | gmlfreq: { 11 | enumerable: true, 12 | get: () => { 13 | return this.params[LoShelfEffectStruct.Index.Freq]; 14 | }, 15 | set: (_freq) => { 16 | const val = this.setParam(LoShelfEffectStruct.Index.Freq, _freq); 17 | 18 | this.nodes.forEach((_node) => { 19 | const freq = _node.parameters.get("freq"); 20 | freq.value = val; 21 | }); 22 | } 23 | }, 24 | gmlq: { 25 | enumerable: true, 26 | get: () => { 27 | return this.params[LoShelfEffectStruct.Index.Q]; 28 | }, 29 | set: (_q) => { 30 | const val = this.setParam(LoShelfEffectStruct.Index.Q, _q); 31 | 32 | this.nodes.forEach((_node) => { 33 | const q = _node.parameters.get("q"); 34 | q.value = val; 35 | }); 36 | } 37 | }, 38 | gmlgain: { 39 | enumerable: true, 40 | get: () => { 41 | return this.params[LoShelfEffectStruct.Index.Gain]; 42 | }, 43 | set: (_gain) => { 44 | const val = this.setParam(LoShelfEffectStruct.Index.Gain, _gain); 45 | 46 | this.nodes.forEach((_node) => { 47 | const gain = _node.parameters.get("gain"); 48 | gain.value = val; 49 | }); 50 | } 51 | } 52 | }); 53 | } 54 | 55 | LoShelfEffectStruct.Index = { 56 | Bypass: 0, 57 | Freq: 1, 58 | Q: 2, 59 | Gain: 3 60 | }; 61 | 62 | LoShelfEffectStruct.ParamDescriptors = [ 63 | { name: "bypass", integer: true, defaultValue: 0, minValue: 0, maxValue: 1 }, 64 | { name: "freq", integer: false, defaultValue: 500.0, minValue: 10.0, maxValue: 20000.0 }, 65 | { name: "q", integer: false, defaultValue: 1.0, minValue: 1.0, maxValue: 100.0 }, 66 | { name: "gain", integer: false, defaultValue: 1e-2, minValue: 1e-6, maxValue: Number.MAX_VALUE } 67 | ]; 68 | // @endif 69 | -------------------------------------------------------------------------------- /scripts/sound/effects/Reverb1.js: -------------------------------------------------------------------------------- 1 | // @if feature("audio_effects") 2 | function Reverb1EffectStruct(_params) { 3 | AudioEffectStruct.call(this, AudioEffect.Type.Reverb1); 4 | Object.setPrototypeOf(this, AudioEffectStruct.prototype); 5 | 6 | this.initParams(_params); 7 | 8 | // Define user-facing properties 9 | Object.defineProperties(this, { 10 | gmlsize: { 11 | enumerable: true, 12 | get: () => { 13 | return this.params[Reverb1EffectStruct.Index.Size]; 14 | }, 15 | set: (_size) => { 16 | const val = this.setParam(Reverb1EffectStruct.Index.Size, _size); 17 | 18 | this.nodes.forEach((_node) => { 19 | const size = _node.parameters.get("size"); 20 | size.value = val; 21 | }); 22 | } 23 | }, 24 | gmldamp: { 25 | enumerable: true, 26 | get: () => { 27 | return this.params[Reverb1EffectStruct.Index.Damp]; 28 | }, 29 | set: (_damp) => { 30 | const val = this.setParam(Reverb1EffectStruct.Index.Damp, _damp); 31 | 32 | this.nodes.forEach((_node) => { 33 | const damp = _node.parameters.get("damp"); 34 | damp.value = val; 35 | }); 36 | } 37 | }, 38 | gmlmix: { 39 | enumerable: true, 40 | get: () => { 41 | return this.params[Reverb1EffectStruct.Index.Mix]; 42 | }, 43 | set: (_mix) => { 44 | const val = this.setParam(Reverb1EffectStruct.Index.Mix, _mix); 45 | 46 | this.nodes.forEach((_node) => { 47 | const mix = _node.parameters.get("mix"); 48 | mix.setTargetAtTime(val, 0, AudioEffect.PARAM_TIME_CONSTANT); 49 | }); 50 | } 51 | } 52 | }); 53 | } 54 | 55 | Reverb1EffectStruct.Index = { 56 | Bypass: 0, 57 | Size: 1, 58 | Damp: 2, 59 | Mix: 3 60 | }; 61 | 62 | Reverb1EffectStruct.ParamDescriptors = [ 63 | { name: "bypass", integer: true, defaultValue: 0, minValue: 0, maxValue: 1 }, 64 | { name: "size", integer: false, defaultValue: 0.7, minValue: 0.0, maxValue: 1.0 }, 65 | { name: "damp", integer: false, defaultValue: 0.1, minValue: 0.0, maxValue: 1.0 }, 66 | { name: "mix", integer: false, defaultValue: 0.35, minValue: 0.0, maxValue: 1.0 } 67 | ]; 68 | // @endif 69 | -------------------------------------------------------------------------------- /scripts/sound/WorkletNodeManager.js: -------------------------------------------------------------------------------- 1 | // @if feature("audio") 2 | function WorkletNodeManager() { 3 | this.nodes = []; 4 | this.handle = setInterval(() => this.cleanup(), 5000); 5 | } 6 | 7 | WorkletNodeManager.prototype.createBusInput = function(_struct) { 8 | const maxChannels = g_WebAudioContext.destination.channelCount; 9 | 10 | const node = new AudioWorkletNode(g_WebAudioContext, "audio-bus-input", { 11 | numberOfInputs: 1, 12 | numberOfOutputs: 2, 13 | outputChannelCount: [maxChannels, maxChannels], 14 | channelCount: maxChannels, 15 | channelCountMode: "explicit" 16 | }); 17 | 18 | this.nodes.push({ 19 | struct: new WeakRef(_struct), 20 | node: node 21 | }); 22 | 23 | return node; 24 | }; 25 | 26 | WorkletNodeManager.prototype.createBusOutput = function(_struct) { 27 | const maxChannels = g_WebAudioContext.destination.channelCount; 28 | 29 | const node = new AudioWorkletNode(g_WebAudioContext, "audio-bus-output", { 30 | numberOfInputs: 2, 31 | numberOfOutputs: 1, 32 | outputChannelCount: [maxChannels], 33 | channelCount: maxChannels, 34 | channelCountMode: "explicit" 35 | }); 36 | 37 | this.nodes.push({ 38 | struct: new WeakRef(_struct), 39 | node: node 40 | }); 41 | 42 | return node; 43 | }; 44 | 45 | WorkletNodeManager.prototype.createEffect = function(_struct) { 46 | const workletName = AudioEffect.getWorkletName(_struct.type); 47 | const maxChannels = g_WebAudioContext.destination.channelCount; 48 | 49 | const node = new AudioWorkletNode(g_WebAudioContext, workletName, { 50 | numberOfInputs: 1, 51 | numberOfOutputs: 1, 52 | outputChannelCount: [maxChannels], 53 | parameterData: _struct.getParamMap(), 54 | channelCount: maxChannels, 55 | channelCountMode: "explicit" 56 | }); 57 | 58 | this.nodes.push({ 59 | struct: new WeakRef(_struct), 60 | node: node 61 | }); 62 | 63 | return node; 64 | }; 65 | 66 | WorkletNodeManager.prototype.cleanup = function() { 67 | this.nodes = this.nodes.filter((_elem) => { 68 | const struct = _elem.struct.deref(); 69 | 70 | if (struct === undefined) 71 | { 72 | _elem.node.port.postMessage("kill"); 73 | return false; 74 | } 75 | 76 | return true; 77 | }); 78 | }; 79 | 80 | WorkletNodeManager.prototype.killNode = function(_node) { 81 | const idx = this.nodes.findIndex(_elem => _elem.node === _node); 82 | 83 | if (idx !== -1) 84 | { 85 | this.nodes[idx].node.port.postMessage("kill"); 86 | this.nodes.splice(idx, 1); 87 | } 88 | }; 89 | 90 | var g_WorkletNodeManager = new WorkletNodeManager(); 91 | // @endif 92 | -------------------------------------------------------------------------------- /scripts/sound/effects/Delay.js: -------------------------------------------------------------------------------- 1 | // @if feature("audio_effects") 2 | function DelayEffectStruct(_params) { 3 | AudioEffectStruct.call(this, AudioEffect.Type.Delay); 4 | Object.setPrototypeOf(this, AudioEffectStruct.prototype); 5 | 6 | this.initParams(_params); 7 | 8 | // Define user-facing properties 9 | Object.defineProperties(this, { 10 | gmltime: { 11 | enumerable: true, 12 | get: () => { 13 | return this.params[DelayEffectStruct.Index.Time]; 14 | }, 15 | set: (_time) => { 16 | const val = this.setParam(DelayEffectStruct.Index.Time, _time); 17 | 18 | this.nodes.forEach((_node) => { 19 | const time = _node.parameters.get("time"); 20 | time.setTargetAtTime(val, 0, AudioEffect.PARAM_TIME_CONSTANT); 21 | }); 22 | } 23 | }, 24 | gmlfeedback: { 25 | enumerable: true, 26 | get: () => { 27 | return this.params[DelayEffectStruct.Index.Feedback]; 28 | }, 29 | set: (_feedback) => { 30 | const val = this.setParam(DelayEffectStruct.Index.Feedback, _feedback); 31 | 32 | this.nodes.forEach((_node) => { 33 | const feedback = _node.parameters.get("feedback"); 34 | feedback.setTargetAtTime(val, 0, AudioEffect.PARAM_TIME_CONSTANT); 35 | }); 36 | } 37 | }, 38 | gmlmix: { 39 | enumerable: true, 40 | get: () => { 41 | return this.params[DelayEffectStruct.Index.Mix]; 42 | }, 43 | set: (_mix) => { 44 | const val = this.setParam(DelayEffectStruct.Index.Mix, _mix); 45 | 46 | this.nodes.forEach((_node) => { 47 | const mix = _node.parameters.get("mix"); 48 | mix.setTargetAtTime(val, 0, AudioEffect.PARAM_TIME_CONSTANT); 49 | }); 50 | } 51 | } 52 | }); 53 | } 54 | 55 | DelayEffectStruct.Index = { 56 | Bypass: 0, 57 | Time: 1, 58 | Feedback: 2, 59 | Mix: 3 60 | }; 61 | 62 | DelayEffectStruct.ParamDescriptors = [ 63 | { name: "bypass", integer: true, defaultValue: 0, minValue: 0, maxValue: 1 }, 64 | { name: "time", integer: false, defaultValue: 0.2, minValue: 0.0, maxValue: 5.0 }, 65 | { name: "feedback", integer: false, defaultValue: 0.5, minValue: 0.0, maxValue: 1.0 }, 66 | { name: "mix", integer: false, defaultValue: 0.35, minValue: 0.0, maxValue: 1.0 } 67 | ]; 68 | // @endif 69 | -------------------------------------------------------------------------------- /scripts/Frustum.js: -------------------------------------------------------------------------------- 1 | var FRUSTUM_PLANE_LEFT = 0; 2 | var FRUSTUM_PLANE_RIGHT = 1; 3 | var FRUSTUM_PLANE_TOP = 2; 4 | var FRUSTUM_PLANE_BOTTOM = 3; 5 | var FRUSTUM_PLANE_NEAR = 4; 6 | var FRUSTUM_PLANE_FAR = 5; 7 | 8 | function Frustum() 9 | { 10 | this.Planes = [ 11 | new Plane(), 12 | new Plane(), 13 | new Plane(), 14 | new Plane(), 15 | new Plane(), 16 | new Plane(), 17 | ]; 18 | } 19 | 20 | /** 21 | * Initializes frustum planes from a view-projection matrix. 22 | * 23 | * @param {Matrix} vp The view-projection matrix. 24 | */ 25 | Frustum.prototype.FromViewProjMat = function (vp) 26 | { 27 | var col1 = new Vector3(vp.m[0], vp.m[4], vp.m[ 8]); 28 | var col2 = new Vector3(vp.m[1], vp.m[5], vp.m[ 9]); 29 | var col3 = new Vector3(vp.m[2], vp.m[6], vp.m[10]); 30 | var col4 = new Vector3(vp.m[3], vp.m[7], vp.m[11]); 31 | 32 | // Find plane magnitudes 33 | this.Planes[FRUSTUM_PLANE_LEFT].Normal = col4.Add(col1); 34 | this.Planes[FRUSTUM_PLANE_RIGHT].Normal = col4.Sub(col1); 35 | this.Planes[FRUSTUM_PLANE_BOTTOM].Normal = col4.Add(col2); 36 | this.Planes[FRUSTUM_PLANE_TOP].Normal = col4.Sub(col2); 37 | this.Planes[FRUSTUM_PLANE_NEAR].Normal = /*_col4.Add(*/col3/*)*/; 38 | this.Planes[FRUSTUM_PLANE_FAR].Normal = col4.Sub(col3); 39 | 40 | // Find plane distances 41 | var _vp12 = vp.m[12]; 42 | var _vp13 = vp.m[13]; 43 | var _vp14 = vp.m[14]; 44 | var _vp15 = vp.m[15]; 45 | 46 | this.Planes[FRUSTUM_PLANE_LEFT].Distance = _vp15 + _vp12; 47 | this.Planes[FRUSTUM_PLANE_RIGHT].Distance = _vp15 - _vp12; 48 | this.Planes[FRUSTUM_PLANE_BOTTOM].Distance = _vp15 + _vp13; 49 | this.Planes[FRUSTUM_PLANE_TOP].Distance = _vp15 - _vp13; 50 | this.Planes[FRUSTUM_PLANE_NEAR].Distance = /*_vp15 +*/ _vp14; 51 | this.Planes[FRUSTUM_PLANE_FAR].Distance = _vp15 - _vp14; 52 | 53 | // Normalize all 6 planes 54 | for (var i = 0; i < 6; ++i) 55 | { 56 | this.Planes[i].Normalise(); 57 | } 58 | }; 59 | 60 | /** 61 | * Tests whether a sphere intersects with the frustum. 62 | * @param {Vector3} position The origin of the sphere. 63 | * @param {Number} radius The sphere's radius. 64 | * @returns True if the sphere intersects with the frustum. 65 | */ 66 | Frustum.prototype.IntersectsSphere = function (position, radius) 67 | { 68 | for (var i = 0; i < 6; ++i) 69 | { 70 | var plane = this.Planes[i]; 71 | var dist = (position.Dot(plane.Normal)) + plane.Distance; 72 | if (dist < -radius) 73 | { 74 | return false; 75 | } 76 | } 77 | return true; 78 | }; 79 | -------------------------------------------------------------------------------- /scripts/sound/AudioPropsCalc.js: -------------------------------------------------------------------------------- 1 | // @if feature("audio") 2 | function AudioPropsCalc() {} 3 | 4 | AudioPropsCalc.invalid_index = -1; 5 | AudioPropsCalc.not_specified = -1; 6 | AudioPropsCalc.default_priority = 0; 7 | AudioPropsCalc.default_loop = false; 8 | AudioPropsCalc.default_gain = 1; 9 | AudioPropsCalc.default_offset = 0; 10 | AudioPropsCalc.default_pitch = 1; 11 | 12 | AudioPropsCalc.CalcGain = function(_voice) { 13 | const asset = AudioPropsCalc.GetAssetProps(_voice.soundid); 14 | const group = AudioPropsCalc.GetGroupProps(_voice.soundid); 15 | // Emitter gains are stored in their own audio node and 16 | // will be multiplied with this value by the audio context 17 | return _voice.gain.get() * asset.gain.get() * group.gain.get(); 18 | }; 19 | 20 | AudioPropsCalc.CalcOffset = function(_voice) { 21 | if (_voice.startoffset == AudioPropsCalc.not_specified) { 22 | const asset = AudioPropsCalc.GetAssetProps(_voice.soundid); 23 | return asset.offset; 24 | } 25 | 26 | return _voice.startoffset; 27 | }; 28 | 29 | AudioPropsCalc.CalcPitch = function(_voice) { 30 | const asset = AudioPropsCalc.GetAssetProps(_voice.soundid); 31 | const emitter = AudioPropsCalc.GetEmitterProps(_voice.pemitter); 32 | 33 | const sourcePitch = _voice.pitch * asset.pitch * emitter.pitch; 34 | const clampedPitch = Math.min(Math.max(1 / 256, sourcePitch), 256); 35 | 36 | if (clampedPitch != sourcePitch) { 37 | console.log("Warning: Source pitch was clipped to %f\n", clampedPitch); 38 | } 39 | 40 | return clampedPitch; 41 | }; 42 | 43 | AudioPropsCalc.GetAssetProps = function(_asset_index) { 44 | const asset = Audio_GetSound(_asset_index); 45 | 46 | if (asset != null) { 47 | return (() => ({ 48 | gain: asset.gain, 49 | offset: asset.trackPos, 50 | pitch: asset.pitch 51 | }))(); 52 | } 53 | 54 | return (() => ({ 55 | gain: new TimeRampedParamLinear(AudioPropsCalc.default_gain), 56 | offset: AudioPropsCalc.default_offset, 57 | pitch: AudioPropsCalc.default_pitch 58 | }))(); 59 | }; 60 | 61 | AudioPropsCalc.GetEmitterProps = function(_emitter) { 62 | if (_emitter != null) { 63 | return (() => ({ 64 | gain: _emitter.gainramp.get(), 65 | pitch: _emitter.pitch 66 | }))(); 67 | } 68 | 69 | return (() => ({ 70 | gain: AudioPropsCalc.default_gain, 71 | pitch: AudioPropsCalc.default_pitch 72 | }))(); 73 | }; 74 | 75 | AudioPropsCalc.GetGroupProps = function(_assetIndex) { 76 | const asset = Audio_GetSound(_assetIndex); 77 | 78 | if (asset != null) { 79 | const group = g_AudioGroups[asset.groupId]; 80 | 81 | if (group !== undefined) { 82 | return (() => ({ 83 | gain: group.gain, 84 | }))(); 85 | } 86 | } 87 | 88 | return (() => ({ 89 | gain: new TimeRampedParamLinear(AudioPropsCalc.default_gain), 90 | }))(); 91 | }; 92 | // @endif audio 93 | -------------------------------------------------------------------------------- /scripts/sound/worklets/AudioBusProcessor.js: -------------------------------------------------------------------------------- 1 | AudioWorkletProcessor.prototype.makeMortal = function() { 2 | this.keepAlive = true; 3 | this.port.onmessage = (_msg) => { 4 | if (_msg.data === "kill") 5 | this.keepAlive = false; 6 | }; 7 | }; 8 | 9 | class AudioBusInput extends AudioWorkletProcessor 10 | { 11 | static get parameterDescriptors() 12 | { 13 | return [ 14 | { name: "bypass", automationRate: "a-rate", defaultValue: 0, minValue: 0, maxValue: 1 } 15 | ]; 16 | } 17 | 18 | constructor() 19 | { 20 | super(); 21 | this.makeMortal(); 22 | } 23 | 24 | process(inputs, outputs, parameters) 25 | { 26 | // 1 input and 2 outputs 27 | // 1st output is written to when not bypassed 28 | // 2nd output is written to when bypassed 29 | const input = inputs[0]; 30 | const bypass = parameters.bypass; 31 | 32 | for (let c = 0; c < input.length; ++c) 33 | { 34 | const inputChannel = input[c]; 35 | 36 | for (let s = 0; s < inputChannel.length; ++s) { 37 | const b = (bypass[s] !== undefined) ? bypass[s] : bypass[0]; 38 | 39 | outputs[b][c][s] = inputChannel[s]; 40 | } 41 | } 42 | 43 | return this.keepAlive; 44 | } 45 | } 46 | 47 | class AudioBusOutput extends AudioWorkletProcessor 48 | { 49 | static get parameterDescriptors() 50 | { 51 | return [ 52 | { name: "gain", automationRate: "a-rate", defaultValue: 1, minValue: 0 } 53 | ]; 54 | } 55 | 56 | constructor() 57 | { 58 | super(); 59 | this.makeMortal(); 60 | } 61 | 62 | process(inputs, outputs, parameters) 63 | { 64 | // 2 inputs and 1 output 65 | // 1st input is from the effect chain 66 | // 2nd input is from the bypass 67 | const input0 = inputs[0]; 68 | const input1 = inputs[1]; 69 | const output = outputs[0]; 70 | 71 | const gain = parameters.gain; 72 | 73 | // Copy the bypassed audio to the output 74 | for (let c = 0; c < input1.length; ++c) 75 | { 76 | const inputChannel = input1[c]; 77 | const outputChannel = output[c]; 78 | 79 | for (let s = 0; s < inputChannel.length; ++s) 80 | outputChannel[s] = inputChannel[s]; 81 | } 82 | 83 | // Then mix in the affected audio with a gain scalar 84 | for (let c = 0; c < input0.length; ++c) 85 | { 86 | const inputChannel = input0[c]; 87 | const outputChannel = output[c]; 88 | 89 | for (let s = 0; s < inputChannel.length; ++s) { 90 | const g = (gain[s] !== undefined) ? gain[s] : gain[0]; 91 | 92 | outputChannel[s] += inputChannel[s] * g; 93 | } 94 | } 95 | 96 | return this.keepAlive; 97 | } 98 | } 99 | 100 | registerProcessor("audio-bus-input", AudioBusInput); 101 | registerProcessor("audio-bus-output", AudioBusOutput); 102 | -------------------------------------------------------------------------------- /scripts/sound/effects/Tremolo.js: -------------------------------------------------------------------------------- 1 | // @if feature("audio_effects") 2 | function TremoloEffectStruct(_params) { 3 | AudioEffectStruct.call(this, AudioEffect.Type.Tremolo); 4 | Object.setPrototypeOf(this, AudioEffectStruct.prototype); 5 | 6 | this.initParams(_params); 7 | 8 | // Define user-facing properties 9 | Object.defineProperties(this, { 10 | gmlrate: { 11 | enumerable: true, 12 | get: () => { 13 | return this.params[TremoloEffectStruct.Index.Rate]; 14 | }, 15 | set: (_rate) => { 16 | const val = this.setParam(TremoloEffectStruct.Index.Rate, _rate); 17 | 18 | this.nodes.forEach((_node) => { 19 | const rate = _node.parameters.get("rate"); 20 | rate.value = val; 21 | }); 22 | } 23 | }, 24 | gmlintensity: { 25 | enumerable: true, 26 | get: () => { 27 | return this.params[TremoloEffectStruct.Index.Intensity]; 28 | }, 29 | set: (_intensity) => { 30 | const val = this.setParam(TremoloEffectStruct.Index.Intensity, _intensity); 31 | 32 | this.nodes.forEach((_node) => { 33 | const intensity = _node.parameters.get("intensity"); 34 | intensity.setTargetAtTime(val, 0, AudioEffect.PARAM_TIME_CONSTANT); 35 | }); 36 | } 37 | }, 38 | gmloffset: { 39 | enumerable: true, 40 | get: () => { 41 | return this.params[TremoloEffectStruct.Index.Offset]; 42 | }, 43 | set: (_offset) => { 44 | const val = this.setParam(TremoloEffectStruct.Index.Offset, _offset); 45 | 46 | this.nodes.forEach((_node) => { 47 | const offset = _node.parameters.get("offset"); 48 | offset.value = val; 49 | }); 50 | } 51 | }, 52 | gmlshape: { 53 | enumerable: true, 54 | get: () => { 55 | return this.params[TremoloEffectStruct.Index.Shape]; 56 | }, 57 | set: (_shape) => { 58 | const val = this.setParam(TremoloEffectStruct.Index.Shape, _shape); 59 | 60 | this.nodes.forEach((_node) => { 61 | const shape = _node.parameters.get("shape"); 62 | shape.value = val; 63 | }); 64 | } 65 | } 66 | }); 67 | } 68 | 69 | TremoloEffectStruct.Index = { 70 | Bypass: 0, 71 | Rate: 1, 72 | Intensity: 2, 73 | Offset: 3, 74 | Shape: 4 75 | }; 76 | 77 | TremoloEffectStruct.ParamDescriptors = [ 78 | { name: "bypass", integer: true, defaultValue: 0, minValue: 0, maxValue: 1 }, 79 | { name: "rate", integer: false, defaultValue: 5.0, minValue: 0.0, maxValue: 20.0 }, 80 | { name: "intensity", integer: false, defaultValue: 1.0, minValue: 0.0, maxValue: 1.0 }, 81 | { name: "offset", integer: false, defaultValue: 0.0, minValue: 0.0, maxValue: 1.0 }, 82 | { name: "shape", integer: true, defaultValue: 0, minValue: 0, maxValue: 4 } 83 | ]; 84 | // @endif 85 | -------------------------------------------------------------------------------- /scripts/sound/effects/Bitcrusher.js: -------------------------------------------------------------------------------- 1 | // @if feature("audio_effects") 2 | function BitcrusherEffectStruct(_params) { 3 | AudioEffectStruct.call(this, AudioEffect.Type.Bitcrusher); 4 | Object.setPrototypeOf(this, AudioEffectStruct.prototype); 5 | 6 | this.initParams(_params); 7 | 8 | // Define user-facing properties 9 | Object.defineProperties(this, { 10 | gmlgain: { 11 | enumerable: true, 12 | get: () => { 13 | return this.params[BitcrusherEffectStruct.Index.Gain]; 14 | }, 15 | set: (_gain) => { 16 | const val = this.setParam(BitcrusherEffectStruct.Index.Gain, _gain); 17 | 18 | this.nodes.forEach((_node) => { 19 | const gain = _node.parameters.get("gain"); 20 | gain.setTargetAtTime(val, 0, AudioEffect.PARAM_TIME_CONSTANT); 21 | }); 22 | } 23 | }, 24 | gmlfactor: { 25 | enumerable: true, 26 | get: () => { 27 | return this.params[BitcrusherEffectStruct.Index.Factor]; 28 | }, 29 | set: (_factor) => { 30 | const val = this.setParam(BitcrusherEffectStruct.Index.Factor, _factor); 31 | 32 | this.nodes.forEach((_node) => { 33 | const factor = _node.parameters.get("factor"); 34 | factor.value = val; 35 | }); 36 | } 37 | }, 38 | gmlresolution: { 39 | enumerable: true, 40 | get: () => { 41 | return this.params[BitcrusherEffectStruct.Index.Resolution]; 42 | }, 43 | set: (_resolution) => { 44 | const val = this.setParam(BitcrusherEffectStruct.Index.Resolution, _resolution); 45 | 46 | this.nodes.forEach((_node) => { 47 | const resolution = _node.parameters.get("resolution"); 48 | resolution.value = val; 49 | }); 50 | } 51 | }, 52 | gmlmix: { 53 | enumerable: true, 54 | get: () => { 55 | return this.params[BitcrusherEffectStruct.Index.Mix]; 56 | }, 57 | set: (_mix) => { 58 | const val = this.setParam(BitcrusherEffectStruct.Index.Mix, _mix); 59 | 60 | this.nodes.forEach((_node) => { 61 | const mix = _node.parameters.get("mix"); 62 | mix.setTargetAtTime(val, 0, AudioEffect.PARAM_TIME_CONSTANT); 63 | }); 64 | } 65 | } 66 | }); 67 | } 68 | 69 | BitcrusherEffectStruct.Index = { 70 | Bypass: 0, 71 | Gain: 1, 72 | Factor: 2, 73 | Resolution: 3, 74 | Mix: 4 75 | }; 76 | 77 | BitcrusherEffectStruct.ParamDescriptors = [ 78 | { name: "bypass", integer: true, defaultValue: 0, minValue: 0, maxValue: 1 }, 79 | { name: "gain", integer: false, defaultValue: 1.0, minValue: 0.0, maxValue: Number.MAX_VALUE }, 80 | { name: "factor", integer: true, defaultValue: 20, minValue: 1, maxValue: 100 }, 81 | { name: "resolution", integer: true, defaultValue: 8, minValue: 2, maxValue: 16 }, 82 | { name: "mix", integer: false, defaultValue: 0.8, minValue: 0.0, maxValue: 1.0 } 83 | ]; 84 | // @endif 85 | -------------------------------------------------------------------------------- /scripts/libWebGL/yySamplerState.js: -------------------------------------------------------------------------------- 1 | // ********************************************************************************************************************** 2 | // 3 | // Copyright (c)2011, YoYo Games Ltd. All Rights reserved. 4 | // 5 | // File: yySamplerState.js 6 | // Created: 16/02/2011 7 | // Author: Mike 8 | // Project: HTML5 9 | // Description: 10 | // 11 | // ********************************************************************************************************************** 12 | 13 | // ############################################################################################# 14 | /// Function: 15 | /// Creates a new container for a texture settings for filtering/wrapping/etc 16 | /// 17 | // ############################################################################################# 18 | /** @constructor */ 19 | function yyTextureSamplerState(_interpolatePixels) { 20 | 21 | var gl = this._gl; 22 | 23 | // Private data 24 | var m_states = []; 25 | 26 | Object.defineProperties(this, { 27 | States: { 28 | get: function () { return m_states; } 29 | } 30 | }); 31 | 32 | // ############################################################################################# 33 | /// Function: 34 | /// Constructor 35 | /// 36 | // ############################################################################################# 37 | (function() { 38 | 39 | if (_interpolatePixels) { 40 | m_states[yyGL.SamplerState_MagFilter] = yyGL.Filter_LinearFiltering; 41 | m_states[yyGL.SamplerState_MinFilter] = yyGL.Filter_LinearFiltering; 42 | m_states[yyGL.SamplerState_MipFilter] = yyGL.Filter_LinearFiltering; 43 | } 44 | else { 45 | m_states[yyGL.SamplerState_MagFilter] = yyGL.Filter_PointFiltering; 46 | m_states[yyGL.SamplerState_MinFilter] = yyGL.Filter_PointFiltering; 47 | m_states[yyGL.SamplerState_MipFilter] = yyGL.Filter_PointFiltering; 48 | } 49 | m_states[yyGL.SamplerState_AddressU] = yyGL.TextureWrap_Clamp; 50 | m_states[yyGL.SamplerState_AddressV] = yyGL.TextureWrap_Clamp; 51 | })(); 52 | 53 | 54 | // ############################################################################################# 55 | /// Function: 56 | /// When creating a texture use this function to set the default texture states 57 | /// for the given target (TEXTURE_2D expected) 58 | /// 59 | // ############################################################################################# 60 | /** @this {yyTextureSamplerState} */ 61 | this.BindDefaults = function (_target) { 62 | 63 | var states = m_states; 64 | gl.texParameteri(_target, gl.TEXTURE_MAG_FILTER, (states[yyGL.SamplerState_MagFilter] == yyGL.Filter_LinearFiltering) ? gl.LINEAR : gl.NEAREST); 65 | gl.texParameteri(_target, gl.TEXTURE_MIN_FILTER, (states[yyGL.SamplerState_MinFilter] == yyGL.Filter_LinearFiltering) ? gl.LINEAR : gl.NEAREST); 66 | 67 | gl.texParameteri(_target, gl.TEXTURE_WRAP_S, (states[yyGL.SamplerState_AddressU] == yyGL.TextureWrap_Clamp) ? gl.CLAMP_TO_EDGE : gl.REPEAT); 68 | gl.texParameteri(_target, gl.TEXTURE_WRAP_T, (states[yyGL.SamplerState_AddressV] == yyGL.TextureWrap_Clamp) ? gl.CLAMP_TO_EDGE : gl.REPEAT); 69 | }; 70 | } 71 | -------------------------------------------------------------------------------- /scripts/yyTrigger.js: -------------------------------------------------------------------------------- 1 | // ********************************************************************************************************************** 2 | // 3 | // Copyright (c)2011, YoYo Games Ltd. All Rights reserved. 4 | // 5 | // File: yyTrigger.js 6 | // Created: 08/10/2011 7 | // Author: Mike 8 | // Project: HTML5 9 | // Description: 10 | // 11 | // Date Version BY Comment 12 | // ---------------------------------------------------------------------------------------------------------------------- 13 | // 08/10/2011 14 | // 15 | // ********************************************************************************************************************** 16 | 17 | 18 | // ############################################################################################# 19 | /// Function: 20 | /// Create a trigger 21 | /// 22 | // ############################################################################################# 23 | /** @constructor */ 24 | function yyTrigger() 25 | { 26 | this.pName = ""; 27 | this.moment = 0; 28 | this.ConstName = ""; 29 | this.pFunc = null; 30 | } 31 | 32 | 33 | 34 | // ############################################################################################# 35 | /// Function: 36 | /// 37 | /// 38 | // ############################################################################################# 39 | /** @constructor */ 40 | function yyTriggerManager( _pTriggers ) 41 | { 42 | if (!_pTriggers) 43 | { 44 | this.pool = []; 45 | } else 46 | { 47 | this.pool = _pTriggers; 48 | } 49 | 50 | } 51 | 52 | 53 | // ############################################################################################# 54 | /// Function: 55 | /// Given the function, return the trigger index. 56 | /// 57 | // ############################################################################################# 58 | yyTriggerManager.prototype.Find = function (_pFunction) { 59 | // Trigger 0 is unused. 60 | for (var i = 1; i < this.pool.length; i++) 61 | { 62 | if (this.pool[i].pFunc == _pFunction) 63 | { 64 | return i; 65 | } 66 | } 67 | return -1; 68 | }; 69 | 70 | 71 | 72 | // ############################################################################################# 73 | /// Function: 74 | /// Given the index, return the trigger. 75 | /// 76 | // ############################################################################################# 77 | yyTriggerManager.prototype.Get = function(_index){ 78 | return this.pool[_index]; 79 | }; 80 | 81 | 82 | // ############################################################################################# 83 | /// Function: 84 | /// Process triggers for a specific "step" 85 | /// 86 | /// 87 | /// In: STEP to process 88 | /// 89 | // ############################################################################################# 90 | yyTriggerManager.prototype.Process = function (_moment) { 91 | 92 | // Loop through all triggers and fire the one's that deal with this "moment". Trigger 0 is unused. 93 | for (var i = 1; i < this.pool.length; i++) 94 | { 95 | var pTrig = this.pool[i]; 96 | if (pTrig.moment == _moment) 97 | { 98 | g_pInstanceManager.PerformEvent(EVENT_TRIGGER, i); 99 | } 100 | } 101 | }; 102 | -------------------------------------------------------------------------------- /scripts/sound/worklets/BitcrusherProcessor.js: -------------------------------------------------------------------------------- 1 | class BitcrusherProcessor extends AudioWorkletProcessor 2 | { 3 | static get parameterDescriptors() 4 | { 5 | return [ 6 | { name: "bypass", automationRate: "a-rate", defaultValue: 0, minValue: 0, maxValue: 1 }, 7 | { name: "gain", automationRate: "a-rate", defaultValue: 1.0, minValue: 0.0 }, 8 | { name: "factor", automationRate: "a-rate", defaultValue: 20, minValue: 1, maxValue: 100 }, 9 | { name: "resolution", automationRate: "a-rate", defaultValue: 8, minValue: 2, maxValue: 16 }, 10 | { name: "mix", automationRate: "a-rate", defaultValue: 0.8, minValue: 0.0, maxValue: 1.0 } 11 | ]; 12 | } 13 | 14 | static scalars = [ 15 | undefined, 16 | undefined, 17 | 2, 18 | 4, 19 | 8, 20 | 16, 21 | 32, 22 | 64, 23 | 128, 24 | 256, 25 | 512, 26 | 1024, 27 | 2048, 28 | 4096, 29 | 8192, 30 | 16384, 31 | 32768 32 | ]; 33 | 34 | constructor(_options) 35 | { 36 | super(); 37 | this.makeMortal(); 38 | 39 | const maxChannels = _options.outputChannelCount[0]; 40 | 41 | this.sample = new Float32Array(maxChannels); 42 | this.hold = new Uint32Array(maxChannels); 43 | } 44 | 45 | process(inputs, outputs, parameters) 46 | { 47 | const input = inputs[0]; 48 | const output = outputs[0]; 49 | 50 | const bypass = parameters.bypass; 51 | const gain = parameters.gain; 52 | const factor = parameters.factor; 53 | const resolution = parameters.resolution; 54 | const mix = parameters.mix; 55 | 56 | for (let c = 0; c < input.length; ++c) { 57 | const inputChannel = input[c]; 58 | const outputChannel = output[c]; 59 | 60 | for (let s = 0; s < inputChannel.length; ++s) { 61 | // Copy the input to the output 62 | outputChannel[s] = inputChannel[s]; 63 | 64 | // Update held sample 65 | if (this.hold[c] === 0) 66 | this.sample[c] = inputChannel[s]; 67 | 68 | // Update hold counter 69 | const f = (factor[s] !== undefined) ? factor[s] : factor[0]; 70 | 71 | ++this.hold[c]; 72 | this.hold[c] %= f; 73 | 74 | // Check bypass state 75 | const b = (bypass[s] !== undefined) ? bypass[s] : bypass[0]; 76 | 77 | if (b > 0.0) { 78 | continue; 79 | } 80 | 81 | // Get the held sample 82 | let val = this.sample[c]; 83 | 84 | // Apply gain and hard clip 85 | const g = (gain[s] !== undefined) ? gain[s] : gain[0]; 86 | 87 | val *= g; 88 | val = Math.max(Math.min(val, 1.0), -1.0); 89 | 90 | // Resolution reduction 91 | const r = (resolution[s] !== undefined) ? resolution[s] : resolution[0]; 92 | const max = (val > 0.0) ? BitcrusherProcessor.scalars[r] - 1 : BitcrusherProcessor.scalars[r]; 93 | 94 | val = Math.round(val * max) / max; 95 | 96 | // Mix the distorted and original samples 97 | const m = (mix[s] !== undefined) ? mix[s] : mix[0]; 98 | 99 | outputChannel[s] *= (1.0 - m); 100 | outputChannel[s] += (val * m); 101 | } 102 | } 103 | 104 | return this.keepAlive; 105 | } 106 | } 107 | 108 | registerProcessor("bitcrusher-processor", BitcrusherProcessor); 109 | -------------------------------------------------------------------------------- /scripts/sound/worklets/DelayProcessor.js: -------------------------------------------------------------------------------- 1 | class DelayProcessor extends AudioWorkletProcessor 2 | { 3 | static MAX_DELAY_TIME = 5.0; // seconds 4 | 5 | static get parameterDescriptors() 6 | { 7 | return [ 8 | { name: "bypass", automationRate: "a-rate", defaultValue: 0, minValue: 0, maxValue: 1 }, 9 | { name: "time", automationRate: "a-rate", defaultValue: 0.2, minValue: 0.0, maxValue: DelayProcessor.MAX_DELAY_TIME }, 10 | { name: "feedback", automationRate: "a-rate", defaultValue: 0.5, minValue: 0.0, maxValue: 1.0 }, 11 | { name: "mix", automationRate: "a-rate", defaultValue: 0.35, minValue: 0.0, maxValue: 1.0 } 12 | ]; 13 | } 14 | 15 | constructor(_options) 16 | { 17 | super(); 18 | this.makeMortal(); 19 | 20 | const maxChannels = _options.outputChannelCount[0]; 21 | 22 | const delayLineLength = (DelayProcessor.MAX_DELAY_TIME * sampleRate) + 1; 23 | 24 | this.buffer = new Array(maxChannels); 25 | this.writeIndex = new Uint32Array(maxChannels); 26 | 27 | for (let c = 0; c < maxChannels; ++c) 28 | this.buffer[c] = new Float32Array(delayLineLength); 29 | } 30 | 31 | process(inputs, outputs, parameters) 32 | { 33 | const input = inputs[0]; 34 | const output = outputs[0]; 35 | 36 | const bypass = parameters.bypass; 37 | const time = parameters.time; 38 | const feedback = parameters.feedback; 39 | const mix = parameters.mix; 40 | 41 | for (let c = 0; c < input.length; ++c) { 42 | const inputChannel = input[c]; 43 | const outputChannel = output[c]; 44 | 45 | for (let s = 0; s < inputChannel.length; ++s) { 46 | // Copy the input to the output 47 | outputChannel[s] = inputChannel[s]; 48 | 49 | // Read a sample from the delay line 50 | const t = (time[s] !== undefined) ? time[s] : time[0]; 51 | 52 | const delayOut = this.read(c, t); 53 | 54 | // Write a sample (with feedback) to the delay line 55 | const f = (feedback[s] !== undefined) ? feedback[s] : feedback[0]; 56 | 57 | const delayIn = inputChannel[s] + (delayOut * f); 58 | this.write(c, delayIn); 59 | 60 | // Check bypass state 61 | const b = (bypass[s] !== undefined) ? bypass[s] : bypass[0]; 62 | 63 | if (b > 0.0) { 64 | continue; 65 | } 66 | 67 | // Mix the delayed and original samples 68 | const m = (mix[s] !== undefined) ? mix[s] : mix[0]; 69 | 70 | outputChannel[s] *= (1 - m); 71 | outputChannel[s] += (delayOut * m); 72 | } 73 | } 74 | 75 | return this.keepAlive; 76 | } 77 | 78 | read(_channel, _time) 79 | { 80 | const delayInFrames = _time * sampleRate; 81 | 82 | let index1 = (this.writeIndex[_channel] - ~~delayInFrames); 83 | let index2 = (index1 - 1); 84 | 85 | while (index1 < 0) 86 | index1 += this.buffer[_channel].length; 87 | 88 | while (index2 < 0) 89 | index2 += this.buffer[_channel].length; 90 | 91 | const frac = delayInFrames - ~~delayInFrames; 92 | 93 | const samp1 = this.buffer[_channel][index1]; 94 | const samp2 = this.buffer[_channel][index2]; 95 | 96 | return samp1 + (samp2 - samp1) * frac; 97 | } 98 | 99 | write(_channel, _sample) 100 | { 101 | ++this.writeIndex[_channel]; 102 | this.writeIndex[_channel] %= this.buffer[_channel].length; 103 | 104 | this.buffer[_channel][this.writeIndex[_channel]] = _sample; 105 | } 106 | } 107 | 108 | registerProcessor("delay-processor", DelayProcessor); 109 | -------------------------------------------------------------------------------- /scripts/libWebGL/shaders/basicShader.js: -------------------------------------------------------------------------------- 1 | // ********************************************************************************************************************** 2 | // 3 | // Copyright (c)2011, YoYo Games Ltd. All Rights reserved. 4 | // 5 | // File: basicShader.js 6 | // Created: 16/02/2011 7 | // Author: Mike 8 | // Project: HTML5 9 | // Description: 10 | // 11 | // ********************************************************************************************************************** 12 | 13 | // ############################################################################################# 14 | /// Function: 15 | /// Provide the source for the built-in basic textured 2D/3D unlit vertex shader 16 | /// 17 | // ############################################################################################# 18 | function getBasicVertexShader() { 19 | 20 | var vsrc = 21 | "#define MATRIX_VIEW 0\n"+ 22 | "#define MATRIX_PROJECTION 1\n"+ 23 | "#define MATRIX_WORLD 2\n"+ 24 | "#define MATRIX_WORLD_VIEW 3\n"+ 25 | "#define MATRIX_WORLD_VIEW_PROJECTION 4\n"+ 26 | "#define MATRICES_MAX 5\n"+ 27 | "#define FOG_SETTINGS 0\n"+ 28 | "#define FOG_COLOUR 1\n"+ 29 | "\n" + 30 | "uniform mat4 matrices[MATRICES_MAX];\n"+ 31 | "uniform vec4 fogParameters[2];\n" + // 0: (enabled, 1/fogRange, blank, blank) 1: fogColour 32 | "\n" + 33 | "attribute vec3 vertex;\n"+ 34 | "attribute vec4 color;\n"+ 35 | "attribute vec2 UV;\n"+ 36 | "\n" + 37 | "varying vec4 fcolor;\n"+ 38 | "varying vec2 texc;\n"+ 39 | "varying vec4 fogColor;\n"+ 40 | "varying float fogFactor;\n"+ 41 | "\n"+ 42 | "float CalcFogFactor(vec4 pos)\n"+ 43 | "{\n"+ 44 | " vec4 viewpos = matrices[MATRIX_WORLD_VIEW] * pos;\n"+ 45 | " vec4 fogParams = fogParameters[FOG_SETTINGS];\n"+ 46 | " return (1.0 - ((fogParams.z - viewpos.z) * fogParams.y)) * fogParams.x;\n"+ 47 | "}\n" + 48 | "\n" + 49 | "void main(void)\n"+ 50 | "{\n"+ 51 | " fcolor = color;\n"+ 52 | " texc = UV;\n"+ 53 | " vec4 pos = vec4(vertex.xyz, 1);\n"+ 54 | " fogFactor = CalcFogFactor(pos);\n"+ 55 | " fogColor = vec4(fogParameters[FOG_COLOUR].xyz, 1);\n"+ 56 | " gl_Position = matrices[MATRIX_WORLD_VIEW_PROJECTION] * pos;\n"+ 57 | " gl_PointSize = 1.0;\n"+ 58 | "}"; 59 | return vsrc; 60 | } 61 | 62 | // ############################################################################################# 63 | /// Function: 64 | /// Provide the source for the built-in basic textured 2D/3D unlit fragment shader 65 | /// 66 | // ############################################################################################# 67 | function getBasicFragmentShader() { 68 | 69 | var fsrc = 70 | "precision highp float;\n" + 71 | "uniform sampler2D pTexure;\n" + 72 | "uniform bool alphaTestEnabled;\n" + 73 | "uniform float alphaRefValue;" + 74 | "\n" + 75 | "varying vec4 fcolor;\n" + 76 | "varying vec2 texc;\n" + 77 | "varying vec4 fogColor;\n" + 78 | "varying float fogFactor;\n" + 79 | "\n" + 80 | "void DoAlphaTest(vec4 SrcColour)\n" + 81 | "{\n" + 82 | " if (alphaTestEnabled)\n" + 83 | " {\n" + 84 | " if (SrcColour.a <= alphaRefValue)\n" + 85 | " {\n" + 86 | " discard;\n" + 87 | " }\n" + 88 | " }\n" + 89 | "}\n" + 90 | "\n" + 91 | "void main(void)\n" + 92 | "{\n" + 93 | " vec4 color = texture2D(pTexure, texc).rgba * fcolor.rgba;\n" + 94 | " DoAlphaTest(color);\n" + 95 | " gl_FragColor = vec4(mix(color.rgb, fogColor.rgb, fogFactor), color.a);\n" + 96 | "}\n"; 97 | return fsrc; 98 | } 99 | -------------------------------------------------------------------------------- /scripts/sound/worklets/LPF2Processor.js: -------------------------------------------------------------------------------- 1 | class LPF2Processor extends AudioWorkletProcessor 2 | { 3 | static get parameterDescriptors() 4 | { 5 | const maxCutoff = sampleRate * 0.45; 6 | 7 | return [ 8 | { name: "bypass", automationRate: "a-rate", defaultValue: 0, minValue: 0, maxValue: 1 }, 9 | { name: "cutoff", automationRate: "a-rate", defaultValue: Math.min(500.0, maxCutoff), minValue: 10.0, maxValue: maxCutoff }, 10 | { name: "q", automationRate: "a-rate", defaultValue: 1.5, minValue: 1.0, maxValue: 100.0 } 11 | ]; 12 | } 13 | 14 | constructor(_options) 15 | { 16 | super(); 17 | this.makeMortal(); 18 | 19 | const maxChannels = _options.outputChannelCount[0]; 20 | 21 | this.a1 = 0; 22 | this.a2 = 0; 23 | this.b0 = 0; 24 | this.b1 = 0; 25 | this.b2 = 0; 26 | 27 | this.x1 = new Float32Array(maxChannels); 28 | this.x2 = new Float32Array(maxChannels); 29 | this.y1 = new Float32Array(maxChannels); 30 | this.y2 = new Float32Array(maxChannels); 31 | 32 | this.prevCutoff = -1; 33 | this.prevQ = -1; 34 | } 35 | 36 | process(inputs, outputs, parameters) 37 | { 38 | const input = inputs[0]; 39 | const output = outputs[0]; 40 | 41 | const bypass = parameters.bypass; 42 | const cutoff = parameters.cutoff; 43 | const q = parameters.q; 44 | 45 | const paramsAreConstant = (cutoff.length === 1 && q.length === 1); 46 | 47 | if (paramsAreConstant) 48 | this.calcCoefficients(cutoff[0], q[0]); 49 | 50 | for (let c = 0; c < input.length; ++c) { 51 | const inputChannel = input[c]; 52 | const outputChannel = output[c]; 53 | 54 | for (let s = 0; s < inputChannel.length; ++s) { 55 | // Recalc coefficients if needed 56 | if (paramsAreConstant === false) { 57 | const c = (cutoff[s] !== undefined) ? cutoff[s] : cutoff[0]; 58 | const qs = (q[s] !== undefined) ? q[s] : q[0]; 59 | 60 | this.calcCoefficients(c, qs); 61 | } 62 | 63 | // Calculate the new sample 64 | const y0 = this.b0 * inputChannel[s] 65 | + this.b1 * this.x1[c] 66 | + this.b2 * this.x2[c] 67 | - this.a1 * this.y1[c] 68 | - this.a2 * this.y2[c]; 69 | 70 | // Shift the original samples 71 | this.x2[c] = this.x1[c]; 72 | this.x1[c] = inputChannel[s]; 73 | 74 | // Shift the filtered samples 75 | this.y2[c] = this.y1[c]; 76 | this.y1[c] = y0; 77 | 78 | // Write the original/filtered sample to the output 79 | const b = (bypass[s] !== undefined) ? bypass[s] : bypass[0]; 80 | 81 | outputChannel[s] = (b > 0) ? inputChannel[s] : y0; 82 | } 83 | } 84 | 85 | return this.keepAlive; 86 | } 87 | 88 | calcCoefficients(_cutoff, _q) 89 | { 90 | if (_cutoff === this.prevCutoff && _q === this.prevQ) 91 | return; 92 | 93 | const w0 = 2 * Math.PI * _cutoff / sampleRate; 94 | 95 | const alpha = Math.sin(w0) / (2 * _q); 96 | const cos_w0 = Math.cos(w0); 97 | 98 | const a0 = 1 + alpha; 99 | const a1 = -2 * cos_w0; 100 | const a2 = 1 - alpha; 101 | 102 | const b0 = (1 - cos_w0) / 2; 103 | const b1 = 1 - cos_w0; 104 | const b2 = (1 - cos_w0) / 2; 105 | 106 | this.a1 = a1 / a0; 107 | this.a2 = a2 / a0; 108 | this.b0 = b0 / a0; 109 | this.b1 = b1 / a0; 110 | this.b2 = b2 / a0; 111 | 112 | this.prevCutoff = _cutoff; 113 | this.prevQ = _q; 114 | } 115 | } 116 | 117 | registerProcessor("lpf2-processor", LPF2Processor); 118 | -------------------------------------------------------------------------------- /scripts/sound/worklets/HPF2Processor.js: -------------------------------------------------------------------------------- 1 | class HPF2Processor extends AudioWorkletProcessor 2 | { 3 | static get parameterDescriptors() 4 | { 5 | const maxCutoff = sampleRate * 0.45; 6 | 7 | return [ 8 | { name: "bypass", automationRate: "a-rate", defaultValue: 0, minValue: 0, maxValue: 1 }, 9 | { name: "cutoff", automationRate: "a-rate", defaultValue: Math.min(1500.0, maxCutoff), minValue: 10.0, maxValue: maxCutoff }, 10 | { name: "q", automationRate: "a-rate", defaultValue: 1.5, minValue: 1.0, maxValue: 100.0 } 11 | ]; 12 | } 13 | 14 | constructor(_options) 15 | { 16 | super(); 17 | this.makeMortal(); 18 | 19 | const maxChannels = _options.outputChannelCount[0]; 20 | 21 | this.a1 = 0; 22 | this.a2 = 0; 23 | this.b0 = 0; 24 | this.b1 = 0; 25 | this.b2 = 0; 26 | 27 | this.x1 = new Float32Array(maxChannels); 28 | this.x2 = new Float32Array(maxChannels); 29 | this.y1 = new Float32Array(maxChannels); 30 | this.y2 = new Float32Array(maxChannels); 31 | 32 | this.prevCutoff = -1; 33 | this.prevQ = -1; 34 | } 35 | 36 | process(inputs, outputs, parameters) 37 | { 38 | const input = inputs[0]; 39 | const output = outputs[0]; 40 | 41 | const bypass = parameters.bypass; 42 | const cutoff = parameters.cutoff; 43 | const q = parameters.q; 44 | 45 | const paramsAreConstant = (cutoff.length === 1 && q.length === 1); 46 | 47 | if (paramsAreConstant) 48 | this.calcCoefficients(cutoff[0], q[0]); 49 | 50 | for (let c = 0; c < input.length; ++c) { 51 | const inputChannel = input[c]; 52 | const outputChannel = output[c]; 53 | 54 | for (let s = 0; s < inputChannel.length; ++s) { 55 | // Recalc coefficients if needed 56 | if (paramsAreConstant === false) { 57 | const c = (cutoff[s] !== undefined) ? cutoff[s] : cutoff[0]; 58 | const qs = (q[s] !== undefined) ? q[s] : q[0]; 59 | 60 | this.calcCoefficients(c, qs); 61 | } 62 | 63 | // Calculate the new sample 64 | const y0 = this.b0 * inputChannel[s] 65 | + this.b1 * this.x1[c] 66 | + this.b2 * this.x2[c] 67 | - this.a1 * this.y1[c] 68 | - this.a2 * this.y2[c]; 69 | 70 | // Shift the original samples 71 | this.x2[c] = this.x1[c]; 72 | this.x1[c] = inputChannel[s]; 73 | 74 | // Shift the filtered samples 75 | this.y2[c] = this.y1[c]; 76 | this.y1[c] = y0; 77 | 78 | // Write the original/filtered sample to the output 79 | const b = (bypass[s] !== undefined) ? bypass[s] : bypass[0]; 80 | 81 | outputChannel[s] = (b > 0) ? inputChannel[s] : y0; 82 | } 83 | } 84 | 85 | return this.keepAlive; 86 | } 87 | 88 | calcCoefficients(_cutoff, _q) 89 | { 90 | if (_cutoff === this.prevCutoff && _q === this.prevQ) 91 | return; 92 | 93 | const w0 = 2 * Math.PI * _cutoff / sampleRate; 94 | 95 | const alpha = Math.sin(w0) / (2 * _q); 96 | const cos_w0 = Math.cos(w0); 97 | 98 | const a0 = 1 + alpha; 99 | const a1 = -2 * cos_w0; 100 | const a2 = 1 - alpha; 101 | 102 | const b0 = (1 + cos_w0) / 2; 103 | const b1 = -1 - cos_w0; 104 | const b2 = (1 + cos_w0) / 2; 105 | 106 | this.a1 = a1 / a0; 107 | this.a2 = a2 / a0; 108 | this.b0 = b0 / a0; 109 | this.b1 = b1 / a0; 110 | this.b2 = b2 / a0; 111 | 112 | this.prevCutoff = _cutoff; 113 | this.prevQ = _q; 114 | } 115 | } 116 | 117 | registerProcessor("hpf2-processor", HPF2Processor); 118 | -------------------------------------------------------------------------------- /scripts/sound/AudioEmitter.js: -------------------------------------------------------------------------------- 1 | // @if feature("audio") 2 | class AudioEmitter { 3 | constructor() { 4 | if (Audio_IsMainBusInitialised() === false) { 5 | console.error("Cannot create audio emitters until audio engine is running - check audio_system_is_initialised()"); 6 | return null; 7 | } 8 | 9 | this.gainramp = new TimeRampedParamLinear(1); 10 | this.gainnode = Audio_CreateGainNode(g_WebAudioContext); 11 | this.pannerNode = AudioEmitter.createPannerNode(); 12 | this.pannerNode.connect(this.gainnode); 13 | 14 | this.reset(); 15 | } 16 | } 17 | 18 | AudioEmitter.createPannerNode = function() { 19 | if (typeof PannerNode === "undefined") { 20 | return g_WebAudioContext.createPanner(); 21 | } 22 | 23 | return new PannerNode(g_WebAudioContext); 24 | }; 25 | 26 | AudioEmitter.prototype.reset = function() { 27 | this.setPosition(0.0, 0.0, 0.01); // why was 'z' not zero? 28 | 29 | this.pannerNode.refDistance = 100.0; 30 | this.pannerNode.maxDistance = 100000.0; 31 | this.pannerNode.rolloffFactor = 1.0; 32 | 33 | this.pannerNode.coneInnerAngle = 360.0; 34 | this.pannerNode.coneOuterAngle = 0.0; 35 | this.pannerNode.coneOuterGain = 0.0; 36 | 37 | this.pannerNode.distanceModel = falloff_model; 38 | this.pannerNode.panningModel = "equalpower"; 39 | 40 | this.gainramp.set(1.0); 41 | this.gainnode.gain.value = 1.0; 42 | 43 | g_AudioBusMain.connectInput(this.gainnode); 44 | this.bus = g_AudioBusMain; 45 | 46 | this.pitch = 1.0; 47 | 48 | if (g_AudioFalloffModel === DistanceModels.AUDIO_FALLOFF_NONE) { 49 | // Workaround for no falloff 50 | this.pannerNode.rolloffFactor = 0.0; 51 | 52 | // Store this value so we can restore it if the falloff model changes later 53 | this.original_rolloffFactor = 1.0; 54 | } 55 | 56 | this.active = true; 57 | }; 58 | 59 | AudioEmitter.prototype.getInput = function() { 60 | return this.pannerNode; 61 | }; 62 | 63 | AudioEmitter.prototype.isActive = function() { 64 | return this.active === true; 65 | }; 66 | 67 | AudioEmitter.prototype.getBus = function() { 68 | return this.bus; 69 | }; 70 | 71 | AudioEmitter.prototype.setBus = function(_bus) { 72 | this.gainnode.disconnect(); 73 | _bus.connectInput(this.gainnode); 74 | this.bus = _bus; 75 | }; 76 | 77 | AudioEmitter.prototype.setFalloff = function(_falloffRef, _falloffMax, _falloffFactor) { 78 | this.pannerNode.refDistance = Math.max(0, _falloffRef); 79 | this.pannerNode.maxDistance = Math.max(Number.MIN_VALUE, _falloffMax); 80 | this.pannerNode.rolloffFactor = Math.max(0, _falloffFactor); 81 | this.pannerNode.distanceModel = falloff_model; 82 | 83 | if (g_AudioFalloffModel === DistanceModels.AUDIO_FALLOFF_NONE) { 84 | this.original_rolloffFactor = this.pannerNode.rolloffFactor; 85 | this.pannerNode.rolloffFactor = 0.0; 86 | } 87 | }; 88 | 89 | AudioEmitter.prototype.getPositionX = function() { 90 | return this.pannerNode.positionX.value; 91 | }; 92 | 93 | AudioEmitter.prototype.getPositionY = function() { 94 | return this.pannerNode.positionY.value; 95 | }; 96 | 97 | AudioEmitter.prototype.getPositionZ = function() { 98 | return this.pannerNode.positionZ.value; 99 | }; 100 | 101 | AudioEmitter.prototype.setPosition = function(_x, _y, _z) { 102 | this.pannerNode.positionX.value = _x; 103 | this.pannerNode.positionY.value = _y; 104 | this.pannerNode.positionZ.value = _z; 105 | }; 106 | 107 | AudioEmitter.prototype.getGain = function() { 108 | return this.gainramp.get(); 109 | }; 110 | 111 | AudioEmitter.prototype.updateGain = function() { 112 | this.gainramp.update(); 113 | if (this.gainnode) 114 | this.gainnode.gain.value = this.gainramp.get(); 115 | }; 116 | 117 | AudioEmitter.prototype.setGain = function(_gain, _timeMs = 0) { 118 | _gain = Math.max(0, _gain); 119 | _timeMs = Math.max(0, _timeMs); 120 | 121 | this.gainramp.set(_gain, _timeMs); 122 | 123 | if (_timeMs === 0) 124 | this.updateGain(); 125 | }; 126 | // @endif audio 127 | -------------------------------------------------------------------------------- /scripts/yyQueue.js: -------------------------------------------------------------------------------- 1 | // ********************************************************************************************************************** 2 | // 3 | // Copyright (c)2011, YoYo Games Ltd. All Rights reserved. 4 | // 5 | // File: yyQueue.js 6 | // Created: 21/10/2011 7 | // Author: Mike 8 | // Project: HTML5 9 | // Description: Simple "queue" 10 | // 11 | // Date Version BY Comment 12 | // ---------------------------------------------------------------------------------------------------------------------- 13 | // 21/10/2011 V1.0 MJD 1st version. 14 | // 15 | // ********************************************************************************************************************** 16 | 17 | // @if function("mp_grid_path") 18 | // ############################################################################################# 19 | /// Function: 20 | /// Create a new QUEUE 21 | /// 22 | // ############################################################################################# 23 | /**@constructor*/ 24 | function yyQueue() 25 | { 26 | this.queue = []; 27 | this.offset = 0; 28 | this.Pop = yyQueue.prototype.Dequeue; 29 | this.Push = yyQueue.prototype.Enqueue; 30 | }; 31 | 32 | // ############################################################################################# 33 | /// Function: 34 | /// Get the length of the queue 35 | /// 36 | // ############################################################################################# 37 | yyQueue.prototype.Length = function () { 38 | return (this.queue.length - this.offset); 39 | }; 40 | 41 | // ############################################################################################# 42 | /// Function: 43 | /// Length at LEAST have "_count" elements in it? 44 | /// 45 | // ############################################################################################# 46 | yyQueue.prototype.AtLeast = function (_count) { 47 | return ((this.queue.length - this.offset) >= _count); 48 | }; 49 | 50 | // ############################################################################################# 51 | /// Function: 52 | /// Is the queue empty? 53 | /// 54 | // ############################################################################################# 55 | yyQueue.prototype.IsEmpty = function () { 56 | return (this.queue.length==0); 57 | }; 58 | 59 | // ############################################################################################# 60 | /// Function: 61 | /// Enqueue an item 62 | /// 63 | // ############################################################################################# 64 | yyQueue.prototype.Enqueue = function (_item) { 65 | this.queue.push(_item); 66 | }; 67 | 68 | // ############################################################################################# 69 | /// Function: 70 | /// Dequeue an item 71 | /// 72 | // ############################################################################################# 73 | yyQueue.prototype.Dequeue = function () 74 | { 75 | if( this.IsEmpty() ) return undefined; 76 | 77 | // get item from start of queue 78 | var item = this.queue[this.offset]; 79 | 80 | // Only cut down on space "now and then", and base it on the size of the queue. 81 | if ((++this.offset * 2) >= this.queue.length) 82 | { 83 | this.queue = this.queue.slice(this.offset); 84 | this.offset = 0; 85 | } 86 | 87 | return item; 88 | }; 89 | 90 | // ############################################################################################# 91 | /// Function: 92 | /// Look at the start of the queue 93 | /// 94 | // ############################################################################################# 95 | yyQueue.prototype.peek = function () { 96 | if (this.queue.length > 0){ 97 | return this.queue[this.offset]; 98 | } else{ 99 | return undefined; 100 | } 101 | }; 102 | // @endif mp 103 | -------------------------------------------------------------------------------- /scripts/sound/AudioPlaybackProps.js: -------------------------------------------------------------------------------- 1 | // @if feature("audio") 2 | var AudioPlaybackType = { 3 | NON_POSITIONAL: 0, 4 | POSITIONAL_SPECIFIED: 1, 5 | POSITIONAL_EMITTER: 2 6 | }; 7 | 8 | function AudioPlaybackProps(_props) { 9 | if (this.getProp(_props, "sound", this, "asset_index", true, yyGetInt32, AudioPropsCalc.invalid_index)) { 10 | this.asset = Audio_GetSound(this.asset_index); 11 | 12 | if (this.asset !== null) { 13 | this.loopStart = this.asset.loopStart; 14 | this.loopEnd = this.asset.loopEnd; 15 | } 16 | } 17 | 18 | if (this.getProp(_props, "emitter", this, "emitter_index", true, yyGetInt32, AudioPropsCalc.invalid_index)) { 19 | this.type = AudioPlaybackType.POSITIONAL_EMITTER; 20 | this.emitter = audio_emitters[this.emitter_index]; 21 | } 22 | 23 | this.getProp(_props, "priority", this, "priority", true, yyGetReal, AudioPropsCalc.default_priority); 24 | this.getProp(_props, "loop", this, "loop", true, yyGetBool, AudioPropsCalc.default_loop); 25 | 26 | this.getProp(_props, "gain", this, "gain", true, yyGetReal, AudioPropsCalc.default_gain); 27 | this.gain = Math.max(0.0, this.gain); 28 | 29 | this.getProp(_props, "offset", this, "offset", true, yyGetReal, AudioPropsCalc.not_specified); 30 | 31 | if (this.offset !== AudioPropsCalc.not_specified) 32 | this.offset = Math.max(0.0, this.offset); 33 | 34 | this.getProp(_props, "pitch", this, "pitch", true, yyGetReal, AudioPropsCalc.default_pitch); 35 | this.pitch = Math.max(Number.MIN_VALUE, this.pitch); 36 | 37 | this.getProp(_props, "position", this, "position", true, undefined, undefined); 38 | if (typeof this.position === "object" && this.type === undefined) { 39 | this.type = AudioPlaybackType.POSITIONAL_SPECIFIED; 40 | 41 | const position = (_props.position !== undefined) ? _props.position : _props.gmlposition; 42 | this.position = {}; 43 | 44 | this.getProp(position, "x", this.position, "x", false, yyGetReal, 0); 45 | this.getProp(position, "y", this.position, "y", false, yyGetReal, 0); 46 | this.getProp(position, "z", this.position, "z", false, yyGetReal, 0); 47 | this.getProp(position, "falloff_ref", this.position, "falloff_ref", true, yyGetReal, 0); 48 | this.getProp(position, "falloff_max", this.position, "falloff_max", true, yyGetReal, 1); 49 | this.getProp(position, "falloff_factor", this.position, "falloff_factor", true, yyGetReal, 1); 50 | } 51 | 52 | if (this.type === undefined) { 53 | this.type = AudioPlaybackType.NON_POSITIONAL; 54 | } 55 | } 56 | 57 | AudioPlaybackProps.prototype.getProp = function(_srcObj, _srcKey, _destObj, _destKey, 58 | _gmlPrefix, _converterFn, _default) { 59 | if (_converterFn === undefined) { 60 | _converterFn = (x) => x; 61 | } 62 | 63 | if (_srcObj[_srcKey] !== undefined) { 64 | _destObj[_destKey] = _converterFn(_srcObj[_srcKey]); 65 | return true; 66 | } 67 | 68 | if (_gmlPrefix && _srcObj["gml" + _srcKey] !== undefined) { 69 | _destObj[_destKey] = _converterFn(_srcObj["gml" + _srcKey]); 70 | return true; 71 | } 72 | 73 | _destObj[_destKey] = _default; 74 | return false; 75 | }; 76 | 77 | AudioPlaybackProps.prototype.invalid = function() { 78 | if (this.asset == null) { 79 | debug("Audio playback failed - invalid asset index: " + this.asset_index); 80 | return true; 81 | } 82 | 83 | if (!audio_group_is_loaded(this.asset.groupId)) { 84 | debug("Error: Audio group for " + audio_get_name(this.asset_index) + " (" + this.asset.groupId + ") is not loaded"); 85 | return true; 86 | } 87 | 88 | if (Audio_IsPlaybackAllowed() === false) { 89 | debug("Audio playback failed. WebAudio Context suspended (user must interact with the page before audio can be played)."); 90 | return true; 91 | } 92 | 93 | if (this.type === AudioPlaybackType.POSITIONAL_EMITTER && this.emitter === undefined) { 94 | debug("Attempting to play sound on inactive emitter: " + this.emitter_index); 95 | return true; 96 | } 97 | 98 | return false; 99 | }; 100 | // @endif audio 101 | -------------------------------------------------------------------------------- /scripts/sound/worklets/TremoloProcessor.js: -------------------------------------------------------------------------------- 1 | class TremoloProcessor extends AudioWorkletProcessor 2 | { 3 | static get parameterDescriptors() { 4 | return [ 5 | { name: "bypass", automationRate: "a-rate", defaultValue: 0, minValue: 0, maxValue: 1 }, 6 | { name: "rate", automationRate: "a-rate", defaultValue: 5.0, minValue: 0.0, maxValue: 20.0 }, 7 | { name: "intensity", automationRate: "a-rate", defaultValue: 1.0, minValue: 0.0, maxValue: 1.0 }, 8 | { name: "offset", automationRate: "a-rate", defaultValue: 0.0, minValue: 0.0, maxValue: 1.0 }, 9 | { name: "shape", automationRate: "a-rate", defaultValue: 0, minValue: 0, maxValue: 4 } 10 | ]; 11 | } 12 | 13 | constructor(_options) { 14 | super(); 15 | this.makeMortal(); 16 | 17 | const maxChannels = _options.outputChannelCount[0]; 18 | 19 | this.prevRate = new Array(maxChannels).fill(1.0); 20 | this.prevOffset = new Array(maxChannels).fill(0.0); 21 | this.prevShape = new Array(maxChannels).fill(LFO.Shape.INV_SAWTOOTH); 22 | 23 | this.lfo = new Array(maxChannels); 24 | 25 | for (let c = 0; c < maxChannels; ++c) { 26 | this.lfo[c] = new WavetableLFO(); 27 | 28 | this.lfo[c].setFs(sampleRate); 29 | this.lfo[c].setFreq(this.prevRate[c]); 30 | this.lfo[c].setShape(this.prevShape[c]); 31 | 32 | if (c % 2 === 1) { 33 | // Only set offset for odd channels 34 | this.lfo[c].setPhaseOffset(this.prevOffset[c]); 35 | } 36 | } 37 | } 38 | 39 | process(inputs, outputs, parameters) { 40 | const input = inputs[0]; 41 | const output = outputs[0]; 42 | 43 | const bypass = parameters.bypass; 44 | const rate = parameters.rate; 45 | const intensity = parameters.intensity; 46 | const offset = parameters.offset; 47 | const shape = parameters.shape; 48 | 49 | for (let c = 0; c < input.length; ++c) { 50 | const inputChannel = input[c]; 51 | const outputChannel = output[c]; 52 | 53 | for (let s = 0; s < inputChannel.length; ++s) { 54 | // Copy the input to the output 55 | outputChannel[s] = inputChannel[s]; 56 | 57 | // Update LFO parameters 58 | const r = (rate[s] !== undefined) ? rate[s] : rate[0]; 59 | const o = (offset[s] !== undefined) ? offset[s] : offset[0]; 60 | const sh = (shape[s] !== undefined) ? shape[s] : shape[0]; 61 | 62 | this.updateLFO(c, r, o, sh); 63 | 64 | // Read (and advance) the LFO 65 | const lfoOut = this.lfo[c].read(); 66 | 67 | // Check bypass state 68 | const b = (bypass[s] !== undefined) ? bypass[s] : bypass[0]; 69 | 70 | if (b > 0.0) { 71 | continue; 72 | } 73 | 74 | // Scale a sample using the LFO output and intensity 75 | const i = (intensity[s] !== undefined) ? intensity[s] : intensity[0]; 76 | 77 | const out = inputChannel[s] * lfoOut * i; 78 | 79 | // Mix the scaled sample with the original sample 80 | outputChannel[s] *= (1.0 - i); 81 | outputChannel[s] += out; 82 | } 83 | } 84 | 85 | return this.keepAlive; 86 | } 87 | 88 | updateLFO(_channel, _rate, _offset, _shape) { 89 | if (_rate !== this.prevRate[_channel]) { 90 | this.lfo[_channel].setFreq(_rate); 91 | this.prevRate[_channel] = _rate; 92 | } 93 | 94 | if (_offset !== this.prevOffset[_channel]) { 95 | // Only set offset for odd channels 96 | if (_channel % 2 === 1) { 97 | this.lfo[_channel].setPhaseOffset(_offset); 98 | } 99 | 100 | this.prevOffset[_channel] = _offset; 101 | } 102 | 103 | if (_shape !== this.prevShape[_channel]) { 104 | this.lfo[_channel].setShape(_shape); 105 | this.prevShape[_channel] = _shape; 106 | } 107 | } 108 | } 109 | 110 | registerProcessor("tremolo-processor", TremoloProcessor); 111 | -------------------------------------------------------------------------------- /scripts/functions/Function_Gamepad.js: -------------------------------------------------------------------------------- 1 | // ********************************************************************************************************************** 2 | // 3 | // Copyright (c)2011, YoYo Games Ltd. All Rights reserved. 4 | // 5 | // File: Function_Gamepad.js 6 | // Created: 16/02/2011 7 | // Author: Mike 8 | // Project: HTML5 9 | // Description: 10 | // 11 | // ********************************************************************************************************************** 12 | 13 | function gamepad_is_supported() { 14 | 15 | return g_pGamepadManager.IsSupported(); 16 | } 17 | 18 | function gamepad_get_device_count() { 19 | 20 | return g_pGamepadManager.GetDeviceCount(); 21 | } 22 | 23 | function gamepad_is_connected(_device) { 24 | 25 | return g_pGamepadManager.IsConnected(yyGetInt32(_device)); 26 | } 27 | 28 | function gamepad_get_button_threshold(_device) { 29 | 30 | return g_pGamepadManager.ButtonThreshold(yyGetInt32(_device)); 31 | } 32 | 33 | function gamepad_set_button_threshold(_device, _threshold) { 34 | 35 | g_pGamepadManager.SetButtonThreshold(yyGetInt32(_device), yyGetReal(_threshold)); 36 | } 37 | 38 | function gamepad_get_axis_deadzone(_device) { 39 | 40 | return g_pGamepadManager.AxisDeadzone(yyGetInt32(_device)); 41 | } 42 | 43 | function gamepad_set_axis_deadzone(_device, _deadzone) { 44 | 45 | g_pGamepadManager.SetAxisDeadzone(yyGetInt32(_device), yyGetReal(_deadzone)); 46 | } 47 | 48 | function gamepad_get_description(_device) { 49 | 50 | return g_pGamepadManager.GetDescription(yyGetInt32(_device)); 51 | } 52 | 53 | function gamepad_button_count(_device) { 54 | 55 | return g_pGamepadManager.ButtonCount(yyGetInt32(_device)); 56 | } 57 | 58 | function gamepad_button_check(_device, _buttonIndex) { 59 | 60 | return g_pGamepadManager.ButtonDown(yyGetInt32(_device), yyGetInt32(_buttonIndex)); 61 | } 62 | 63 | function gamepad_button_check_pressed(_device, _buttonIndex) { 64 | 65 | return g_pGamepadManager.ButtonPressed(yyGetInt32(_device), yyGetInt32(_buttonIndex)); 66 | } 67 | 68 | function gamepad_button_check_released(_device, _buttonIndex) { 69 | 70 | return g_pGamepadManager.ButtonReleased(yyGetInt32(_device), yyGetInt32(_buttonIndex)); 71 | } 72 | 73 | function gamepad_button_value(_device, _buttonIndex) { 74 | 75 | return g_pGamepadManager.ButtonValue(yyGetInt32(_device), yyGetInt32(_buttonIndex)); 76 | } 77 | 78 | function gamepad_axis_count(_device) { 79 | 80 | return g_pGamepadManager.AxisCount(yyGetInt32(_device)); 81 | } 82 | 83 | function gamepad_axis_value(_device, _axisIndex) { 84 | 85 | return g_pGamepadManager.AxisValue(yyGetInt32(_device), yyGetInt32(_axisIndex)); 86 | } 87 | 88 | function gamepad_set_vibration(_device, _leftMotor, _rightMotor) { 89 | } 90 | 91 | function gamepad_set_color(_device,_color){ 92 | } 93 | 94 | function gamepad_set_colour(_device,_colour){ 95 | } 96 | 97 | function gamepad_hat_count(_device){ 98 | // RK :: HTML5 controllers do not expose hats 99 | return 0; 100 | } 101 | 102 | function gamepad_hat_value(_device, _index){ 103 | // RK :: HTML5 controllers do not expose hats 104 | return 0; 105 | } 106 | 107 | function gamepad_remove_mapping(_device){ 108 | // RK :: HTML5 controllers do not have any mapping functionality 109 | } 110 | 111 | function gamepad_test_mapping(_device, _mapping){ 112 | // RK :: HTML5 controllers do not have any mapping functionality 113 | } 114 | 115 | function gamepad_get_mapping(_device) { 116 | // RK :: HTML5 controllers do not have any mapping functionality 117 | _device = yyGetInt32(_device); 118 | if ((_device < 0 ) || (_device >= g_pGamepadManager.GetDeviceCount()) ) { 119 | return "device index out of range"; 120 | } // end if 121 | return "no mapping"; 122 | } 123 | 124 | function gamepad_get_guid(_device){ 125 | // RK :: HTML5 controllers do not have any mapping functionality 126 | _device = yyGetInt32(_device); 127 | if ((_device < 0 ) || (_device >= g_pGamepadManager.GetDeviceCount()) ) { 128 | return "device index out of range"; 129 | } // end if 130 | return "none"; 131 | } 132 | 133 | function gamepad_set_option() { 134 | 135 | } 136 | 137 | function gamepad_get_option() { 138 | return 0; 139 | } 140 | 141 | 142 | 143 | -------------------------------------------------------------------------------- /scripts/sound/worklets/WavetableLFO.js: -------------------------------------------------------------------------------- 1 | function LFO() {} 2 | 3 | LFO.Shape = { 4 | INV_SAWTOOTH: 0, 5 | SAWTOOTH: 1, 6 | SINE: 2, 7 | SQUARE: 3, 8 | TRIANGLE: 4, 9 | NUM_SHAPES: 5 10 | }; 11 | 12 | /* 13 | LFO Wavetable Generators ~ 14 | These functions generate a 1Hz waveform from a normalised phase parameter (range 0 to 1). 15 | The generated waveforms also range from 0 to 1. 16 | The wavetables produced by these functions should only be used for LFOs, 17 | as reading them at higher frequencies will begin to cause aliasing. 18 | */ 19 | LFO.generateInvSawtooth = function(_phase) { 20 | return 1.0 - _phase; 21 | }; 22 | 23 | LFO.generateSawtooth = function(_phase) { 24 | return _phase; 25 | }; 26 | 27 | LFO.generateSine = function(_phase) { 28 | // Offset by (-pi/2) in order to start from 0 29 | return 0.5 * (Math.sin((_phase * 2.0 * Math.PI) - (Math.PI / 2.0)) + 1.0); 30 | }; 31 | 32 | LFO.generateSquare = function(_phase) { 33 | if (_phase < 0.5) { 34 | return 0.0; 35 | } 36 | 37 | return 1.0; 38 | }; 39 | 40 | LFO.generateTriangle = function(_phase) { 41 | if (_phase < 0.5) { 42 | return 2.0 * _phase; 43 | } 44 | 45 | return 2.0 - (2.0 * _phase); 46 | }; 47 | 48 | LFO.wavetableFns = [ 49 | LFO.generateInvSawtooth, 50 | LFO.generateSawtooth, 51 | LFO.generateSine, 52 | LFO.generateSquare, 53 | LFO.generateTriangle 54 | ]; 55 | 56 | Wavetable.len = 512; 57 | Wavetable.phaseInc = 1.0 / Wavetable.len; 58 | 59 | function Wavetable(_fn) { 60 | this.data = new Float32Array(Wavetable.len); 61 | 62 | for (let i = 0; i < Wavetable.len; ++i) { 63 | this.data[i] = _fn(i * Wavetable.phaseInc); 64 | } 65 | } 66 | 67 | Wavetable.prototype.read = function(_phase) { 68 | _phase = Math.max(0.0, _phase); 69 | _phase = Math.min(_phase, 1.0); 70 | 71 | const targetIndex = _phase * Wavetable.len; 72 | 73 | const targetIndexInt = ~~targetIndex; 74 | const targetIndexFrac = targetIndex - targetIndexInt; 75 | 76 | let index1 = targetIndexInt; 77 | let index2 = index1 + 1; 78 | 79 | if (index1 >= Wavetable.len) { 80 | index1 -= Wavetable.len; 81 | } 82 | 83 | if (index2 >= Wavetable.len) { 84 | index2 -= Wavetable.len; 85 | } 86 | 87 | const samp1 = this.data[index1]; 88 | const samp2 = this.data[index2]; 89 | 90 | return samp1 + (samp2 - samp1) * targetIndexFrac; 91 | }; 92 | 93 | WavetableLFO.wavetables = []; 94 | WavetableLFO.initialisedWavetables = false; 95 | 96 | WavetableLFO.minFreq = 0.0; 97 | WavetableLFO.maxFreq = 20.0; 98 | 99 | function WavetableLFO() { 100 | this.fs = 48000; 101 | this.shape = LFO.Shape.SINE; 102 | this.freq = 1.0; 103 | this.phase = 0.0; 104 | this.phaseInc = 0.0; 105 | this.phaseOffset = 0.0; 106 | 107 | if (WavetableLFO.initialisedWavetables == true) { 108 | return; 109 | } 110 | 111 | for (let i = 0; i < LFO.Shape.NUM_SHAPES; ++i) { 112 | WavetableLFO.wavetables[i] = new Wavetable(LFO.wavetableFns[i]); 113 | } 114 | 115 | WavetableLFO.initialisedWavetables = true; 116 | } 117 | 118 | WavetableLFO.isInitialised = function() { 119 | return (WavetableLFO.initialisedWavetables == true); 120 | }; 121 | 122 | WavetableLFO.prototype.setFs = function(_fs) { 123 | this.fs = _fs; 124 | this.updatePhaseInc(); 125 | }; 126 | 127 | WavetableLFO.prototype.setFreq = function(_freq) { 128 | _freq = Math.max(WavetableLFO.minFreq, _freq); 129 | _freq = Math.min(_freq, WavetableLFO.maxFreq); 130 | 131 | this.freq = _freq; 132 | this.updatePhaseInc(); 133 | }; 134 | 135 | WavetableLFO.prototype.setPhaseOffset = function(_offset) { 136 | _offset = Math.max(0.0, _offset); 137 | _offset = Math.min(_offset, 1.0); 138 | 139 | const diff = _offset - this.phaseOffset; 140 | 141 | this.phaseOffset = _offset; 142 | this.phase += diff; 143 | 144 | while (this.phase >= 1.0) { 145 | this.phase -= 1.0; 146 | } 147 | 148 | while (this.phase < 0.0) { 149 | this.phase += 1.0; 150 | } 151 | }; 152 | 153 | WavetableLFO.prototype.setShape = function(_shape) { 154 | _shape = Math.max(0, _shape); 155 | _shape = Math.min(_shape, LFO.Shape.NUM_SHAPES - 1); 156 | 157 | this.shape = _shape; 158 | }; 159 | 160 | WavetableLFO.prototype.read = function() { 161 | const result = WavetableLFO.wavetables[this.shape].read(this.phase); 162 | 163 | this.phase += this.phaseInc; 164 | 165 | while (this.phase >= 1.0) { 166 | this.phase -= 1.0; 167 | } 168 | 169 | return result; 170 | }; 171 | 172 | WavetableLFO.prototype.updatePhaseInc = function() { 173 | this.phaseInc = this.freq / this.fs; 174 | }; 175 | -------------------------------------------------------------------------------- /scripts/sound/worklets/PeakEQProcessor.js: -------------------------------------------------------------------------------- 1 | class PeakEQProcessor extends AudioWorkletProcessor 2 | { 3 | static get parameterDescriptors() 4 | { 5 | const maxFreq = sampleRate * 0.45; 6 | 7 | return [ 8 | { name: "bypass", automationRate: "a-rate", defaultValue: 0, minValue: 0, maxValue: 1 }, 9 | { name: "freq", automationRate: "a-rate", defaultValue: Math.min(1500.0, maxFreq), minValue: 10.0, maxValue: maxFreq }, 10 | { name: "q", automationRate: "a-rate", defaultValue: 1.0, minValue: 1.0, maxValue: 100.0 }, 11 | { name: "gain", automationRate: "a-rate", defaultValue: 1e-2, minValue: 1e-6 } 12 | ]; 13 | } 14 | 15 | constructor(_options) 16 | { 17 | super(); 18 | this.makeMortal(); 19 | 20 | const maxChannels = _options.outputChannelCount[0]; 21 | 22 | this.a1 = 0; 23 | this.a2 = 0; 24 | this.b0 = 0; 25 | this.b1 = 0; 26 | this.b2 = 0; 27 | 28 | this.x1 = new Float32Array(maxChannels); 29 | this.x2 = new Float32Array(maxChannels); 30 | this.y1 = new Float32Array(maxChannels); 31 | this.y2 = new Float32Array(maxChannels); 32 | 33 | this.prevFreq = -1; 34 | this.prevQ = -1; 35 | this.prevGain = -1; 36 | } 37 | 38 | process(inputs, outputs, parameters) 39 | { 40 | const input = inputs[0]; 41 | const output = outputs[0]; 42 | 43 | const bypass = parameters.bypass; 44 | const freq = parameters.freq; 45 | const q = parameters.q; 46 | const gain = parameters.gain; 47 | 48 | const paramsAreConstant = (freq.length === 1 && q.length === 1 && gain.length === 1); 49 | 50 | if (paramsAreConstant) 51 | this.calcCoefficients(freq[0], q[0], gain[0]); 52 | 53 | for (let c = 0; c < input.length; ++c) { 54 | const inputChannel = input[c]; 55 | const outputChannel = output[c]; 56 | 57 | for (let s = 0; s < inputChannel.length; ++s) { 58 | // Recalc coefficients if needed 59 | if (paramsAreConstant === false) { 60 | const f = (freq[s] !== undefined) ? freq[s] : freq[0]; 61 | const qs = (q[s] !== undefined) ? q[s] : q[0]; 62 | const g = (gain[s] !== undefined) ? gain[s] : gain[0]; 63 | 64 | this.calcCoefficients(f, qs, g); 65 | } 66 | 67 | // Calculate the new sample 68 | const y0 = this.b0 * inputChannel[s] 69 | + this.b1 * this.x1[c] 70 | + this.b2 * this.x2[c] 71 | - this.a1 * this.y1[c] 72 | - this.a2 * this.y2[c]; 73 | 74 | // Shift the original samples 75 | this.x2[c] = this.x1[c]; 76 | this.x1[c] = inputChannel[s]; 77 | 78 | // Shift the filtered samples 79 | this.y2[c] = this.y1[c]; 80 | this.y1[c] = y0; 81 | 82 | // Write the original/filtered sample to the output 83 | const b = (bypass[s] !== undefined) ? bypass[s] : bypass[0]; 84 | 85 | outputChannel[s] = (b > 0) ? inputChannel[s] : y0; 86 | } 87 | } 88 | 89 | return this.keepAlive; 90 | } 91 | 92 | calcCoefficients(_freq, _q, _gain) 93 | { 94 | if (_freq === this.prevFreq && _q === this.prevQ && _gain === this.prevGain) 95 | return; 96 | 97 | const w0 = 2 * Math.PI * _freq / sampleRate; 98 | 99 | const cos_w0 = Math.cos(w0); 100 | const A = Math.sqrt(_gain); 101 | 102 | const alpha = Math.sin(w0) / (2 * _q); 103 | const alpha_a = alpha / A; 104 | const alpha_b = alpha * A; 105 | 106 | const a0 = 1 + alpha_a; 107 | const a1 = -2 * cos_w0; 108 | const a2 = 1 - alpha_a; 109 | 110 | const b0 = 1 + alpha_b; 111 | const b1 = a1; 112 | const b2 = 1 - alpha_b; 113 | 114 | this.a1 = a1 / a0; 115 | this.a2 = a2 / a0; 116 | this.b0 = b0 / a0; 117 | this.b1 = b1 / a0; 118 | this.b2 = b2 / a0; 119 | 120 | this.prevFreq = _freq; 121 | this.prevQ = _q; 122 | this.prevGain = _gain; 123 | } 124 | } 125 | 126 | registerProcessor("peak-eq-processor", PeakEQProcessor); 127 | -------------------------------------------------------------------------------- /scripts/sound/effects/Compressor.js: -------------------------------------------------------------------------------- 1 | // @if feature("audio_effects") 2 | function CompressorEffectStruct(_params) { 3 | AudioEffectStruct.call(this, AudioEffect.Type.Compressor); 4 | Object.setPrototypeOf(this, AudioEffectStruct.prototype); 5 | 6 | this.initParams(_params); 7 | 8 | Object.defineProperties(this, { 9 | gmlingain: { 10 | enumerable: true, 11 | get: () => { 12 | return this.params[CompressorEffectStruct.Index.InGain]; 13 | }, 14 | set: (_ingain) => { 15 | const val = this.setParam(CompressorEffectStruct.Index.InGain, _ingain); 16 | 17 | this.nodes.forEach((_node) => { 18 | const ingain = _node.parameters.get("ingain"); 19 | ingain.value = val; 20 | }); 21 | } 22 | }, 23 | gmlthreshold: { 24 | enumerable: true, 25 | get: () => { 26 | return this.params[CompressorEffectStruct.Index.Threshold]; 27 | }, 28 | set: (_threshold) => { 29 | const val = this.setParam(CompressorEffectStruct.Index.Threshold, _threshold); 30 | 31 | this.nodes.forEach((_node) => { 32 | const threshold = _node.parameters.get("threshold"); 33 | threshold.value = val; 34 | }); 35 | } 36 | }, 37 | gmlratio: { 38 | enumerable: true, 39 | get: () => { 40 | return this.params[CompressorEffectStruct.Index.Ratio]; 41 | }, 42 | set: (_ratio) => { 43 | const val = this.setParam(CompressorEffectStruct.Index.Ratio, _ratio); 44 | 45 | this.nodes.forEach((_node) => { 46 | const ratio = _node.parameters.get("ratio"); 47 | ratio.value = val; 48 | }); 49 | } 50 | }, 51 | gmlattack: { 52 | enumerable: true, 53 | get: () => { 54 | return this.params[CompressorEffectStruct.Index.Attack]; 55 | }, 56 | set: (_attack) => { 57 | const val = this.setParam(CompressorEffectStruct.Index.Attack, _attack); 58 | 59 | this.nodes.forEach((_node) => { 60 | const attack = _node.parameters.get("attack"); 61 | attack.value = val; 62 | }); 63 | } 64 | }, 65 | gmlrelease: { 66 | enumerable: true, 67 | get: () => { 68 | return this.params[CompressorEffectStruct.Index.Release]; 69 | }, 70 | set: (_release) => { 71 | const val = this.setParam(CompressorEffectStruct.Index.Release, _release); 72 | 73 | this.nodes.forEach((_node) => { 74 | const release = _node.parameters.get("release"); 75 | release.value = val; 76 | }); 77 | } 78 | }, 79 | gmloutgain: { 80 | enumerable: true, 81 | get: () => { 82 | return this.params[CompressorEffectStruct.Index.OutGain]; 83 | }, 84 | set: (_outgain) => { 85 | const val = this.setParam(CompressorEffectStruct.Index.OutGain, _outgain); 86 | 87 | this.nodes.forEach((_node) => { 88 | const outgain = _node.parameters.get("outgain"); 89 | outgain.value = val; 90 | }); 91 | } 92 | } 93 | }); 94 | } 95 | 96 | CompressorEffectStruct.Index = { 97 | Bypass: 0, 98 | InGain: 1, 99 | Threshold: 2, 100 | Ratio: 3, 101 | Attack: 4, 102 | Release: 5, 103 | OutGain: 6 104 | }; 105 | 106 | CompressorEffectStruct.ParamDescriptors = [ 107 | { name: "bypass", integer: true, defaultValue: 0, minValue: 0, maxValue: 1 }, 108 | { name: "ingain", integer: false, defaultValue: 1, minValue: 1e-6, maxValue: Number.MAX_VALUE }, 109 | { name: "threshold", integer: false, defaultValue: 0.125, minValue: 1e-3, maxValue: 1 }, 110 | { name: "ratio", integer: false, defaultValue: 4, minValue: 1, maxValue: Number.MAX_VALUE }, 111 | { name: "attack", integer: false, defaultValue: 0.05, minValue: 1e-3, maxValue: 1e-1}, 112 | { name: "release", integer: false, defaultValue: 0.25, minValue: 1e-2, maxValue: 1 }, 113 | { name: "outgain", integer: false, defaultValue: 1, minValue: 1e-6, maxValue: Number.MAX_VALUE } 114 | ]; 115 | // @endif 116 | -------------------------------------------------------------------------------- /scripts/sound/worklets/HiShelfProcessor.js: -------------------------------------------------------------------------------- 1 | class HiShelfProcessor extends AudioWorkletProcessor 2 | { 3 | static get parameterDescriptors() 4 | { 5 | const maxFreq = sampleRate * 0.45; 6 | 7 | return [ 8 | { name: "bypass", automationRate: "a-rate", defaultValue: 0, minValue: 0, maxValue: 1 }, 9 | { name: "freq", automationRate: "a-rate", defaultValue: Math.min(5000.0, maxFreq), minValue: 10.0, maxValue: maxFreq }, 10 | { name: "q", automationRate: "a-rate", defaultValue: 1.0, minValue: 1.0, maxValue: 100.0 }, 11 | { name: "gain", automationRate: "a-rate", defaultValue: 1e-2, minValue: 1e-6 } 12 | ]; 13 | } 14 | 15 | constructor(_options) 16 | { 17 | super(); 18 | this.makeMortal(); 19 | 20 | const maxChannels = _options.outputChannelCount[0]; 21 | 22 | this.a1 = 0; 23 | this.a2 = 0; 24 | this.b0 = 0; 25 | this.b1 = 0; 26 | this.b2 = 0; 27 | 28 | this.x1 = new Float32Array(maxChannels); 29 | this.x2 = new Float32Array(maxChannels); 30 | this.y1 = new Float32Array(maxChannels); 31 | this.y2 = new Float32Array(maxChannels); 32 | 33 | this.prevFreq = -1; 34 | this.prevQ = -1; 35 | this.prevGain = -1; 36 | } 37 | 38 | process(inputs, outputs, parameters) 39 | { 40 | const input = inputs[0]; 41 | const output = outputs[0]; 42 | 43 | const bypass = parameters.bypass; 44 | const freq = parameters.freq; 45 | const q = parameters.q; 46 | const gain = parameters.gain; 47 | 48 | const paramsAreConstant = (freq.length === 1 && q.length === 1 && gain.length === 1); 49 | 50 | if (paramsAreConstant) 51 | this.calcCoefficients(freq[0], q[0], gain[0]); 52 | 53 | for (let c = 0; c < input.length; ++c) { 54 | const inputChannel = input[c]; 55 | const outputChannel = output[c]; 56 | 57 | for (let s = 0; s < inputChannel.length; ++s) { 58 | // Recalc coefficients if needed 59 | if (paramsAreConstant === false) { 60 | const f = (freq[s] !== undefined) ? freq[s] : freq[0]; 61 | const qs = (q[s] !== undefined) ? q[s] : q[0]; 62 | const g = (gain[s] !== undefined) ? gain[s] : gain[0]; 63 | 64 | this.calcCoefficients(f, qs, g); 65 | } 66 | 67 | // Calculate the new sample 68 | const y0 = this.b0 * inputChannel[s] 69 | + this.b1 * this.x1[c] 70 | + this.b2 * this.x2[c] 71 | - this.a1 * this.y1[c] 72 | - this.a2 * this.y2[c]; 73 | 74 | // Shift the original samples 75 | this.x2[c] = this.x1[c]; 76 | this.x1[c] = inputChannel[s]; 77 | 78 | // Shift the filtered samples 79 | this.y2[c] = this.y1[c]; 80 | this.y1[c] = y0; 81 | 82 | // Write the original/filtered sample to the output 83 | const b = (bypass[s] !== undefined) ? bypass[s] : bypass[0]; 84 | 85 | outputChannel[s] = (b > 0) ? inputChannel[s] : y0; 86 | } 87 | } 88 | 89 | return this.keepAlive; 90 | } 91 | 92 | calcCoefficients(_freq, _q, _gain) 93 | { 94 | if (_freq === this.prevFreq && _q === this.prevQ && _gain === this.prevGain) 95 | return; 96 | 97 | const w0 = 2 * Math.PI * _freq / sampleRate; 98 | const cos_w0 = Math.cos(w0); 99 | 100 | const A = Math.sqrt(_gain); 101 | const Ap1 = A + 1; 102 | const Am1 = A - 1; 103 | 104 | const Ap1_cos_w0 = Ap1 * cos_w0; 105 | const Am1_cos_w0 = Am1 * cos_w0; 106 | 107 | const Ap1_m_Am1_cos_w0 = Ap1 - Am1_cos_w0; 108 | const Ap1_p_Am1_cos_w0 = Ap1 + Am1_cos_w0; 109 | 110 | const alpha = Math.sin(w0) / (2 * _q); 111 | const _2_sqrt_A_alpha = (2 * Math.sqrt(A) * alpha); 112 | 113 | const a0 = Ap1_m_Am1_cos_w0 + _2_sqrt_A_alpha; 114 | const a1 = 2 * (Am1 - Ap1_cos_w0); 115 | const a2 = Ap1_m_Am1_cos_w0 - _2_sqrt_A_alpha; 116 | 117 | const b0 = A * (Ap1_p_Am1_cos_w0 + _2_sqrt_A_alpha); 118 | const b1 = -2 * A * (Am1 + Ap1_cos_w0); 119 | const b2 = A * (Ap1_p_Am1_cos_w0 - _2_sqrt_A_alpha); 120 | 121 | this.a1 = a1 / a0; 122 | this.a2 = a2 / a0; 123 | this.b0 = b0 / a0; 124 | this.b1 = b1 / a0; 125 | this.b2 = b2 / a0; 126 | 127 | this.prevFreq = _freq; 128 | this.prevQ = _q; 129 | this.prevGain = _gain; 130 | } 131 | } 132 | 133 | registerProcessor("hi-shelf-processor", HiShelfProcessor); 134 | -------------------------------------------------------------------------------- /scripts/sound/worklets/LoShelfProcessor.js: -------------------------------------------------------------------------------- 1 | class LoShelfProcessor extends AudioWorkletProcessor 2 | { 3 | static get parameterDescriptors() 4 | { 5 | const maxFreq = sampleRate * 0.45; 6 | 7 | return [ 8 | { name: "bypass", automationRate: "a-rate", defaultValue: 0, minValue: 0, maxValue: 1 }, 9 | { name: "freq", automationRate: "a-rate", defaultValue: Math.min(500.0, maxFreq), minValue: 10.0, maxValue: maxFreq }, 10 | { name: "q", automationRate: "a-rate", defaultValue: 1.0, minValue: 1.0, maxValue: 100.0 }, 11 | { name: "gain", automationRate: "a-rate", defaultValue: 1e-2, minValue: 1e-6 } 12 | ]; 13 | } 14 | 15 | constructor(_options) 16 | { 17 | super(); 18 | this.makeMortal(); 19 | 20 | const maxChannels = _options.outputChannelCount[0]; 21 | 22 | this.a1 = 0; 23 | this.a2 = 0; 24 | this.b0 = 0; 25 | this.b1 = 0; 26 | this.b2 = 0; 27 | 28 | this.x1 = new Float32Array(maxChannels); 29 | this.x2 = new Float32Array(maxChannels); 30 | this.y1 = new Float32Array(maxChannels); 31 | this.y2 = new Float32Array(maxChannels); 32 | 33 | this.prevFreq = -1; 34 | this.prevQ = -1; 35 | this.prevGain = -1; 36 | } 37 | 38 | process(inputs, outputs, parameters) 39 | { 40 | const input = inputs[0]; 41 | const output = outputs[0]; 42 | 43 | const bypass = parameters.bypass; 44 | const freq = parameters.freq; 45 | const q = parameters.q; 46 | const gain = parameters.gain; 47 | 48 | const paramsAreConstant = (freq.length === 1 && q.length === 1 && gain.length === 1); 49 | 50 | if (paramsAreConstant) 51 | this.calcCoefficients(freq[0], q[0], gain[0]); 52 | 53 | for (let c = 0; c < input.length; ++c) { 54 | const inputChannel = input[c]; 55 | const outputChannel = output[c]; 56 | 57 | for (let s = 0; s < inputChannel.length; ++s) { 58 | // Recalc coefficients if needed 59 | if (paramsAreConstant === false) { 60 | const f = (freq[s] !== undefined) ? freq[s] : freq[0]; 61 | const qs = (q[s] !== undefined) ? q[s] : q[0]; 62 | const g = (gain[s] !== undefined) ? gain[s] : gain[0]; 63 | 64 | this.calcCoefficients(f, qs, g); 65 | } 66 | 67 | // Calculate the new sample 68 | const y0 = this.b0 * inputChannel[s] 69 | + this.b1 * this.x1[c] 70 | + this.b2 * this.x2[c] 71 | - this.a1 * this.y1[c] 72 | - this.a2 * this.y2[c]; 73 | 74 | // Shift the original samples 75 | this.x2[c] = this.x1[c]; 76 | this.x1[c] = inputChannel[s]; 77 | 78 | // Shift the filtered samples 79 | this.y2[c] = this.y1[c]; 80 | this.y1[c] = y0; 81 | 82 | // Write the original/filtered sample to the output 83 | const b = (bypass[s] !== undefined) ? bypass[s] : bypass[0]; 84 | 85 | outputChannel[s] = (b > 0) ? inputChannel[s] : y0; 86 | } 87 | } 88 | 89 | return this.keepAlive; 90 | } 91 | 92 | calcCoefficients(_freq, _q, _gain) 93 | { 94 | if (_freq === this.prevFreq && _q === this.prevQ && _gain === this.prevGain) 95 | return; 96 | 97 | const w0 = 2 * Math.PI * _freq / sampleRate; 98 | const cos_w0 = Math.cos(w0); 99 | 100 | const A = Math.sqrt(_gain); 101 | const Ap1 = A + 1; 102 | const Am1 = A - 1; 103 | 104 | const Ap1_cos_w0 = Ap1 * cos_w0; 105 | const Am1_cos_w0 = Am1 * cos_w0; 106 | 107 | const Ap1_m_Am1_cos_w0 = Ap1 - Am1_cos_w0; 108 | const Ap1_p_Am1_cos_w0 = Ap1 + Am1_cos_w0; 109 | 110 | const alpha = Math.sin(w0) / (2 * _q); 111 | const _2_sqrt_A_alpha = (2 * Math.sqrt(A) * alpha); 112 | 113 | const a0 = Ap1_p_Am1_cos_w0 + _2_sqrt_A_alpha; 114 | const a1 = -2 * (Am1 + Ap1_cos_w0); 115 | const a2 = Ap1_p_Am1_cos_w0 - _2_sqrt_A_alpha; 116 | 117 | const b0 = A * (Ap1_m_Am1_cos_w0 + _2_sqrt_A_alpha); 118 | const b1 = 2 * A * (Am1 - Ap1_cos_w0); 119 | const b2 = A * (Ap1_m_Am1_cos_w0 - _2_sqrt_A_alpha); 120 | 121 | this.a1 = a1 / a0; 122 | this.a2 = a2 / a0; 123 | this.b0 = b0 / a0; 124 | this.b1 = b1 / a0; 125 | this.b2 = b2 / a0; 126 | 127 | this.prevFreq = _freq; 128 | this.prevQ = _q; 129 | this.prevGain = _gain; 130 | } 131 | } 132 | 133 | registerProcessor("lo-shelf-processor", LoShelfProcessor); 134 | -------------------------------------------------------------------------------- /scripts/Storage.js: -------------------------------------------------------------------------------- 1 | // ********************************************************************************************************************** 2 | // 3 | // Copyright (c)2011, YoYo Games Ltd. All Rights reserved. 4 | // 5 | // File: Storage.js 6 | // Created: 16/02/2011 7 | // Author: Mike 8 | // Project: HTML5 9 | // Description: 10 | // 11 | // ********************************************************************************************************************** 12 | 13 | 14 | // complicated by the fact that images may have been scaled before being placed on the TPage 15 | // then they are cropped ... 16 | // so the scale can be calculated by ow/w or oh/h 17 | // the actual width and height come from the CropWidth and CropHeight 18 | /** @constructor */ 19 | function yyTPageEntry() 20 | { 21 | this.x = 0; // x position (in texels) on the texture 22 | this.y = 0; // y position (in texels) on the texture 23 | this.w = 0; // width before cropping and after scaling (in texels) of the image 24 | this.h = 0; // height before cropping and after scaling (in texels) of the image 25 | this.XOffset = 0; // X offset (in texels) into the image based on the original size. 26 | this.YOffset = 0; // Y offset (in texels) into the image based on the original size. 27 | this.CropWidth = 0; // width (in texels) of the image 28 | this.CropHeight = 0; // height (in texels) of the image 29 | this.ow = 0; // original width (texels) 30 | this.oh = 0; // original height (texels) 31 | this.tp = 0; // texture page index 32 | this.copy = TPE_Copy; 33 | } 34 | function yyTPageEntryUserCopy() 35 | { 36 | } 37 | // Copy a texture page entry. 38 | /** @constructor */ 39 | function TPE_Copy(_pTPE) 40 | { 41 | this.x = _pTPE.x; 42 | this.y = _pTPE.y; 43 | this.w = _pTPE.w; 44 | this.h = _pTPE.h; 45 | this.XOffset = _pTPE.XOffset; 46 | this.YOffset = _pTPE.YOffset; 47 | this.CropWidth = _pTPE.CropWidth; 48 | this.CropHeight = _pTPE.CropHeight; 49 | this.ow = _pTPE.ow; 50 | this.oh = _pTPE.oh; 51 | this.tp = _pTPE.tp; 52 | } 53 | 54 | 55 | // @if feature("sprites") 56 | /** @constructor */ 57 | function YYSprite() 58 | { 59 | this.pName = ""; 60 | this.width = 0; 61 | this.height = 0; 62 | this.bboxLeft = 0; 63 | this.bboxRight = 0; 64 | this.bboxBottom = 0; 65 | this.bboxTop = 0; 66 | this.transparent = 0; 67 | this.smooth = 0; 68 | this.preload = 0; 69 | this.bboxMode = 0; 70 | this.colCheck = 0; 71 | this.xOrigin = 0; 72 | this.yOrigin = 0; 73 | this.TPageEntrys = []; // followed by count * (YYTPageEntry*) 74 | } 75 | // @endif sprites 76 | 77 | 78 | /** @constructor */ 79 | function YYObjectStorage() 80 | { 81 | this.pName = ""; 82 | this.spriteIndex = 0; 83 | this.visible = true; 84 | this.solid = true; 85 | this.depth = 0; 86 | this.persistent = false; 87 | this.parent = -1; 88 | this.spritemask = false; 89 | } 90 | 91 | 92 | /** @constructor */ 93 | function YYInstancesStorage() 94 | { 95 | this.x = 0; 96 | this.y = 0; 97 | this.index = 0; 98 | this.id = 0; 99 | this.pCode = null; 100 | } 101 | 102 | 103 | /** @constructor */ 104 | function YYViewStorage() 105 | { 106 | this.visible = false; 107 | this.xview = 0; 108 | this.yview = 0; 109 | this.wview = 0; 110 | this.hview = 0; 111 | this.xport = 0; 112 | this.yport = 0; 113 | this.wport = 0; 114 | this.hport = 0; 115 | this.hborder = 0; 116 | this.vborder = 0; 117 | this.hspeed = 0; 118 | this.vspeed = 0; 119 | this.index = 0; 120 | } 121 | 122 | 123 | /** @constructor */ 124 | function YYBackgroundStorage() 125 | { 126 | this.visible = false; 127 | this.foreground = 0; 128 | this.index = 0; 129 | this.x = 0; 130 | this.y = 0; 131 | this.hTiled = 0; 132 | this.vTiled = 0; 133 | this.hSpeed = 0; 134 | this.vSpeed = 0; 135 | this.stretch = 0; 136 | } 137 | 138 | /** @constructor */ 139 | function yyRoomStorage() 140 | { 141 | this.pName = ""; 142 | this.pCaption = "Game Maker Room"; 143 | this.width = 640; 144 | this.height = 480; 145 | this.speed = 30; 146 | this.persistent = false; 147 | this.colour = 0; 148 | this.showColour = false; 149 | this.pCode=null; 150 | this.enableViews = false; 151 | 152 | this.pInstances = []; // Array of YYRoomInstances 153 | this.pBackgrounds = []; // Array of YYRoomBackgrounds 154 | this.pViews = []; // Array of YYRoomViews 155 | this.pTiles = []; // Array of YYRoomTiles 156 | } 157 | 158 | 159 | 160 | /** @constructor */ 161 | function yyGameFile() 162 | { 163 | this.GMObjects = []; // YYObjectStorage 164 | this.GMRooms = []; 165 | this.Textures = []; // texture pages 166 | this.Sprites = []; // Sprites! 167 | } 168 | 169 | 170 | -------------------------------------------------------------------------------- /scripts/libWebGL/yyGLTexture.js: -------------------------------------------------------------------------------- 1 | // ********************************************************************************************************************** 2 | // 3 | // Copyright (c)2011, YoYo Games Ltd. All Rights reserved. 4 | // 5 | // File: yyGLTexture.js 6 | // Created: 16/02/2011 7 | // Author: Mike 8 | // Project: HTML5 9 | // Description: 10 | // 11 | // ********************************************************************************************************************** 12 | 13 | /** @constructor */ 14 | function yyGLTexture(_glTexture, _width, _height, _pow2, _image, _numMips, _flags, _format) { 15 | 16 | var gl = this._gl; 17 | 18 | var m_samplerState, 19 | m_texture, 20 | m_width, 21 | m_height, 22 | m_pow2, 23 | m_image, 24 | m_numMips, 25 | m_flags, 26 | m_format; 27 | 28 | var m_isDirty; 29 | 30 | Object.defineProperties(this, { 31 | SamplerState: { 32 | get: function () { return m_samplerState; } 33 | }, 34 | Texture: { 35 | get: function () { return m_texture; }, 36 | set: function (_val) { m_texture = _val; } 37 | }, 38 | Width: { 39 | get: function () { return m_width; } 40 | }, 41 | Height: { 42 | get: function () { return m_height; } 43 | }, 44 | Pow2: { 45 | get: function () { return m_pow2; } 46 | }, 47 | Image: { 48 | get: function () { return m_image; } 49 | }, 50 | NumMips: { 51 | get: function () { return m_numMips; } 52 | }, 53 | Flags: { 54 | get: function () { return m_flags; }, 55 | set: function (_val) { m_flags = _val; } 56 | }, 57 | IsDirty: { 58 | get: function () { return m_isDirty; }, 59 | set: function (_val) { m_isDirty = _val; } 60 | }, 61 | Format: { 62 | get: function () { return m_format; } 63 | }, 64 | }); 65 | 66 | // ############################################################################################# 67 | /// Function: 68 | /// Construction 69 | /// 70 | // ############################################################################################# 71 | (function () { 72 | 73 | m_texture = _glTexture; 74 | m_width = _width; 75 | m_height = _height; 76 | m_pow2 = _pow2; 77 | m_image = _image; 78 | m_numMips = _numMips; 79 | m_flags = _flags; 80 | m_format = _format; 81 | m_isDirty = false; 82 | 83 | if (m_format == undefined) 84 | m_format = eTextureFormat_A8R8G8B8; 85 | 86 | m_samplerState = new yyTextureSamplerState(); 87 | 88 | if (m_texture) 89 | { 90 | m_samplerState.BindDefaults(gl.TEXTURE_2D); 91 | } 92 | 93 | m_flags = eInternalTextureFlags.NoFlags; 94 | 95 | if (m_numMips !== undefined 96 | && m_numMips !== 0) 97 | { 98 | m_flags |= (eInternalTextureFlags.GenerateMips | eInternalTextureFlags.SupportsMips); 99 | } 100 | 101 | })(); 102 | } 103 | 104 | // ############################################################################################# 105 | /// Enum: 106 | /// Texture flags and options 107 | /// 108 | // ############################################################################################# 109 | var eTextureFlags = 110 | { 111 | NoFlags: 0, 112 | Dirty: 1, 113 | RenderTarget: 2, 114 | DepthBuffer: 4, 115 | RenderTargetLinear: 8, 116 | Hack: 16, 117 | GenerateMips: 32, 118 | ScreenTex: 64 119 | }; 120 | Object.freeze(eTextureFlags); 121 | 122 | var eInternalTextureFlagBits = 123 | { 124 | Dirty: 0, 125 | NoScaling: 1, 126 | NPOT: 2, 127 | RenderTarget: 3, 128 | GenerateMips: 4, 129 | HasMips: 5, 130 | SupportsMips: 6, 131 | IsReady: 7, 132 | ScreenTex: 8, 133 | DepthBuffer: 9, 134 | Plat: 16, 135 | //Beware beyond here 136 | }; 137 | Object.freeze(eInternalTextureFlagBits); 138 | 139 | var eInternalTextureFlags = 140 | { 141 | NoFlags : 0, 142 | Dirty : (1 << eInternalTextureFlagBits.Dirty), 143 | NoScaling : (1 << eInternalTextureFlagBits.NoScaling), 144 | NPOT : (1 << eInternalTextureFlagBits.NPOT), 145 | RenderTarget : (1 << eInternalTextureFlagBits.RenderTarget), 146 | GenerateMips : (1 << eInternalTextureFlagBits.GenerateMips), 147 | HasMips : (1 << eInternalTextureFlagBits.HasMips), 148 | SupportsMips : (1 << eInternalTextureFlagBits.SupportsMips), 149 | IsReady : (1 << eInternalTextureFlagBits.IsReady), 150 | ScreenTex : (1 << eInternalTextureFlagBits.ScreenTex) 151 | }; 152 | Object.freeze(eInternalTextureFlags); -------------------------------------------------------------------------------- /scripts/sound/worklets/CompressorProcessor.js: -------------------------------------------------------------------------------- 1 | class Env { 2 | constructor(_time = 1e-3) { 3 | this.setTime(_time); 4 | } 5 | 6 | /* Time to move ~63.2% towards target value */ 7 | setTime(_time) { 8 | this.tc = Math.exp(-1 / (_time * sampleRate)); 9 | } 10 | 11 | process(_in, _prev) { 12 | return _in + this.tc * (_prev - _in); 13 | } 14 | } 15 | 16 | class AttRelEnv { 17 | constructor(_att, _rel) { 18 | this.att = new Env(_att); 19 | this.rel = new Env(_rel); 20 | 21 | this.prevAtt = _att; 22 | this.prevRel = _rel; 23 | } 24 | 25 | setAtt(_time) { 26 | if (_time === this.prevAtt) 27 | return; 28 | 29 | this.att.setTime(_time); 30 | this.prevAtt = _time; 31 | } 32 | 33 | setRel(_time) { 34 | if (_time === this.prevRel) 35 | return; 36 | 37 | this.rel.setTime(_time); 38 | this.prevRel = _time; 39 | } 40 | 41 | process(_in, _prev) { 42 | if (_in > _prev) 43 | return this.att.process(_in, _prev); 44 | else 45 | return this.rel.process(_in, _prev); 46 | } 47 | } 48 | 49 | class CompressorProcessor extends AudioWorkletProcessor { 50 | static get parameterDescriptors() { 51 | return [ 52 | { name: "bypass", automationRate: "a-rate", defaultValue: 0, minValue: 0, maxValue: 1 }, 53 | { name: "ingain", automationRate: "a-rate", defaultValue: 1, minValue: 0 }, 54 | { name: "threshold", automationRate: "a-rate", defaultValue: 0.125, minValue: 1e-3, maxValue: 1 }, 55 | { name: "ratio", automationRate: "a-rate", defaultValue: 4, minValue: 1 }, 56 | { name: "attack", automationRate: "a-rate", defaultValue: 0.05, minValue: 1e-3, maxValue: 1e-1 }, 57 | { name: "release", automationRate: "a-rate", defaultValue: 0.25, minValue: 1e-2, maxValue: 1 }, 58 | { name: "outgain", automationRate: "a-rate", defaultValue: 1, minValue: 0 } 59 | ]; 60 | } 61 | 62 | constructor(_opts) { 63 | super(); 64 | this.makeMortal(); 65 | 66 | const att = CompressorProcessor.parameterDescriptors.find(_p => _p.name === "attack"); 67 | const rel = CompressorProcessor.parameterDescriptors.find(_p => _p.name === "release"); 68 | 69 | this.env = new AttRelEnv(att.defaultValue, rel.defaultValue); 70 | this.excessSmoothed = 0; 71 | } 72 | 73 | process(_ins, _outs, _params) { 74 | const input = _ins[0]; 75 | const output = _outs[0]; 76 | 77 | const bypass = _params.bypass; 78 | const ingain = _params.ingain; 79 | const outgain = _params.outgain; 80 | const threshold = _params.threshold; 81 | const ratio = _params.ratio; 82 | const attack = _params.attack; 83 | const release = _params.release; 84 | 85 | if (input.length === 0) 86 | return this.keepAlive; 87 | 88 | for (let s = 0; s < input[0].length; ++s) { 89 | /* Create frame */ 90 | let frame = input.map(_c => _c[s]); 91 | 92 | /* Copy in => out */ 93 | output.forEach((_c, _i) => { 94 | _c[s] = frame[_i]; 95 | }); 96 | 97 | /* Input gain */ 98 | const ig = (ingain[s] !== undefined) ? ingain[s] : ingain[0]; 99 | frame = frame.map(_s => _s *= ig); 100 | 101 | /* Calc excess */ 102 | const rect = frame.map(_s => Math.abs(_s)); 103 | const max = Math.max(...rect); 104 | const maxdB = linToDb(max); 105 | const t = (threshold[s] !== undefined) ? threshold[s] : threshold[0]; 106 | const tdB = linToDb(t); 107 | const excess = Math.max(0, maxdB - tdB); 108 | 109 | /* Smooth excess */ 110 | const att = (attack[s] !== undefined) ? attack[s] : attack[0]; 111 | const rel = (release[s] !== undefined) ? release[s] : release[0]; 112 | this.env.setAtt(att); 113 | this.env.setRel(rel); 114 | this.excessSmoothed = this.env.process(excess, this.excessSmoothed); 115 | 116 | /* Check bypass */ 117 | const b = (bypass[s] !== undefined) ? bypass[s] : bypass[0]; 118 | 119 | if (b > 0) 120 | continue; 121 | 122 | /* Gain reduction */ 123 | const r = (ratio[s] !== undefined) ? ratio[s] : ratio[0]; 124 | const gdB = (this.excessSmoothed / r) - this.excessSmoothed; 125 | const g = dbToLin(gdB); 126 | frame = frame.map(_s => _s *= g); 127 | 128 | /* Output gain */ 129 | const og = (outgain[s] !== undefined) ? outgain[s] : outgain[0]; 130 | frame = frame.map(_s => _s *= og); 131 | 132 | /* Write frame */ 133 | output.forEach((_c, _i) => { 134 | _c[s] = frame[_i]; 135 | }); 136 | } 137 | 138 | return this.keepAlive; 139 | } 140 | } 141 | 142 | function linToDb(_x) { 143 | return 20 * Math.log10(_x); 144 | } 145 | 146 | function dbToLin(_x) { 147 | return Math.pow(10, _x / 20); 148 | } 149 | 150 | registerProcessor("compressor-processor", CompressorProcessor); 151 | -------------------------------------------------------------------------------- /scripts/yyWeakRef.js: -------------------------------------------------------------------------------- 1 | // ********************************************************************************************************************** 2 | // 3 | // Copyright (c)2020, YoYo Games Ltd. All Rights reserved. 4 | // 5 | // File: yyWeakRef.js 6 | // Created: 24/07/2020 7 | // Author: MikeR 8 | // Project: HTML5 9 | // Description: 10 | // 11 | // Date Version BY Comment 12 | // ---------------------------------------------------------------------------------------------------------------------- 13 | // 24/07/2020 14 | // 15 | // ********************************************************************************************************************** 16 | 17 | // ############################################################################################# 18 | /// Function: 19 | /// Constructor for the yyWeakRef object 20 | /// 21 | // ############################################################################################# 22 | /** @constructor */ 23 | function yyWeakRef() 24 | { 25 | this.__yyIsGMLObject = true; 26 | this.__type = "[weakref]"; 27 | 28 | this.pWeakRef = null; 29 | 30 | this.IsRefAlive = function () 31 | { 32 | if (this.pWeakRef == null) 33 | { 34 | return undefined; 35 | } 36 | else if (this.pWeakRef.deref() == undefined) 37 | { 38 | return false; 39 | } 40 | 41 | return true; 42 | }; 43 | 44 | this.SetReference = function (_pRef) 45 | { 46 | if (typeof WeakRef === "undefined") 47 | { 48 | this.pWeakRef = null; 49 | } 50 | else 51 | { 52 | this.pWeakRef = new WeakRef(_pRef); 53 | } 54 | 55 | }; 56 | 57 | Object.defineProperties(this, { 58 | gmlref: { 59 | enumerable: true, 60 | get: function () { return (this.pWeakRef == null) ? undefined : ((this.pWeakRef.deref() == undefined) ? undefined : this.pWeakRef.deref()); } 61 | } 62 | }); 63 | } 64 | 65 | function weak_ref_create(_pRef) 66 | { 67 | if (_pRef != undefined) 68 | { 69 | if ((typeof (_pRef) == "object") || (typeof (_pRef) == "function")) 70 | { 71 | var weakref = new yyWeakRef(); 72 | weakref.SetReference(_pRef); 73 | return weakref; 74 | } 75 | else 76 | { 77 | yyError("invalid argument passed to weak_ref_create"); 78 | } 79 | } 80 | else 81 | { 82 | yyError("incorrect number of arguments to weak_ref_create"); 83 | } 84 | 85 | return undefined; 86 | } 87 | 88 | function weak_ref_alive(_pWeakRef) 89 | { 90 | if (_pWeakRef != undefined) 91 | { 92 | if ((typeof (_pWeakRef) == "object") && (_pWeakRef.__type != undefined) && (_pWeakRef.__type == "[weakref]")) 93 | { 94 | return _pWeakRef.IsRefAlive(); 95 | } 96 | return undefined; 97 | } 98 | else 99 | { 100 | yyError("incorrect number of arguments to weak_ref_alive"); 101 | } 102 | } 103 | 104 | function weak_ref_any_alive(_array, _index, _length) 105 | { 106 | if (_array == undefined) 107 | { 108 | yyError("incorrect number of arguments to weak_ref_any_alive"); 109 | } 110 | else if (Array.isArray(_array) == false) 111 | { 112 | yyError("first argument to weak_ref_any_alive is not an array"); 113 | } 114 | else 115 | { 116 | var index = 0; 117 | var length = _array.length; 118 | 119 | if (_index != undefined) 120 | { 121 | index = _index; 122 | } 123 | if (_length != undefined) 124 | { 125 | length = _length; 126 | } 127 | 128 | // Clamp values to array size 129 | if (index < 0) 130 | { 131 | length += index; 132 | index = 0; 133 | } 134 | if (index >= _array.length) 135 | { 136 | return; 137 | } 138 | if ((index + length) >= _array.length) 139 | { 140 | length = _array.length - index; 141 | } 142 | if (length <= 0) 143 | { 144 | return; 145 | } 146 | 147 | if (length > 0) 148 | { 149 | var res = false; 150 | 151 | var end = index + length; 152 | for (var i = index; i < end; i++) 153 | { 154 | var entry = _array[i]; 155 | 156 | if ((typeof (entry) == "object") && (entry.__type != undefined) && (entry.__type == "[weakref]")) 157 | { 158 | var isalive = entry.IsRefAlive(); 159 | if (isalive == undefined) 160 | { 161 | return undefined; 162 | } 163 | else if (entry.IsRefAlive() == true) 164 | { 165 | res = true; 166 | } 167 | } 168 | else 169 | { 170 | return undefined; 171 | } 172 | } 173 | 174 | return res; 175 | } 176 | } 177 | 178 | return undefined; 179 | } -------------------------------------------------------------------------------- /scripts/functions/Function_Misc.js: -------------------------------------------------------------------------------- 1 | // ********************************************************************************************************************** 2 | // Copyright (C) 2019, YoYo Games Ltd. All Rights reserved. 3 | // ********************************************************************************************************************** 4 | 5 | function extension_exists(_extension_name) 6 | { 7 | _extension_name = yyGetString(_extension_name); 8 | // Search for the first element that matches the predicate 9 | const extension = g_pGMFile.Extensions.find(entry => entry["name"] == _extension_name); 10 | return extension !== undefined; 11 | } 12 | 13 | function extension_get_version(_extension_name) 14 | { 15 | _extension_name = yyGetString(_extension_name); 16 | try { 17 | // Search for the first element that matches the predicate 18 | const extension = g_pGMFile.Extensions.find(entry => entry["name"] == _extension_name); 19 | // Returns 'version' if found, else return 'undefined' 20 | if (extension === undefined) return undefined; 21 | return extension.version; 22 | 23 | } catch( _ex ) { 24 | show_debug_message( "extension_get_version :: caught unhandled exception " + _ex.message ); 25 | } // end catch 26 | 27 | return undefined; 28 | } 29 | 30 | function extension_get_option_value(_extension_name, _option_name) 31 | { 32 | _extension_name = yyGetString(_extension_name); 33 | _option_name = yyGetString(_option_name); 34 | 35 | try { 36 | if( g_pGMFile.ExtensionOptions !== undefined ) 37 | { 38 | if (g_pGMFile.ExtensionOptions[_extension_name] !== undefined) 39 | { 40 | return g_pGMFile.ExtensionOptions[_extension_name][_option_name]; 41 | } 42 | } 43 | } catch( _ex ) { 44 | show_debug_message( "extension_get_option_value :: caught unhandled exception " + _ex.message ); 45 | } // end catch 46 | 47 | return undefined; 48 | } 49 | 50 | function extension_get_option_count(_extension_name) 51 | { 52 | _extension_name = yyGetString(_extension_name); 53 | try { 54 | if( g_pGMFile.ExtensionOptions !== undefined ) 55 | { 56 | if (g_pGMFile.ExtensionOptions[_extension_name] !== undefined) 57 | { 58 | return Object.keys(g_pGMFile.ExtensionOptions[_extension_name]).length; 59 | } 60 | } 61 | } catch( _ex ) { 62 | show_debug_message( "extension_get_option_count :: caught unhandled exception " + _ex.message ); 63 | } // end catch 64 | 65 | return 0; 66 | } 67 | 68 | function extension_get_option_names(_extension_name) 69 | { 70 | _extension_name = yyGetString(_extension_name); 71 | try { 72 | if( g_pGMFile.ExtensionOptions !== undefined ) 73 | { 74 | if (g_pGMFile.ExtensionOptions[_extension_name] !== undefined) 75 | { 76 | return Object.keys(g_pGMFile.ExtensionOptions[_extension_name]); 77 | } 78 | } 79 | } catch( _ex ) { 80 | show_debug_message( "extension_get_option_names :: caught unhandled exception " + _ex.message ); 81 | } // end catch 82 | 83 | return []; 84 | } 85 | 86 | function extension_get_options(_extension_name) 87 | { 88 | _extension_name = yyGetString(_extension_name); 89 | try { 90 | if( g_pGMFile.ExtensionOptions !== undefined ) 91 | { 92 | if (g_pGMFile.ExtensionOptions[_extension_name] !== undefined) 93 | { 94 | var data = g_pGMFile.ExtensionOptions[_extension_name]; 95 | var optionData = new GMLObject(); 96 | Object.keys(data).forEach(element => { 97 | variable_struct_set(optionData, element, data[element]) 98 | }); 99 | 100 | return optionData; 101 | } 102 | } 103 | } catch( _ex ) { 104 | show_debug_message( "extension_get_options :: caught unhandled exception " + _ex.message ); 105 | } // end catch 106 | 107 | return {}; 108 | } 109 | 110 | function gc_collect() 111 | { 112 | ErrorFunction("gc_collect"); 113 | } 114 | 115 | function gc_enable(_enable) 116 | { 117 | ErrorFunction("gc_enable"); 118 | } 119 | 120 | function gc_is_enabled() 121 | { 122 | ErrorFunction("gc_is_enabled"); 123 | return true; 124 | } 125 | 126 | function gc_get_stats() 127 | { 128 | var resobj = new Object(); 129 | resobj.__yyIsGMLObject = true; 130 | 131 | Object.defineProperties(resobj, { 132 | gmlobjects_touched: { enumerable: true, get: function () { return 0; } }, 133 | gmlobjects_collected: { enumerable: true, get: function () { return 0; } }, 134 | gmltraversal_time: { enumerable: true, get: function () { return 0; } }, 135 | gmlcollection_time: { enumerable: true, get: function () { return 0; } }, 136 | gmlgeneration_collected: { enumerable: true, get: function () { return 0; } }, 137 | gmlgc_frame: { enumerable: true, get: function () { return 0; } }, 138 | gmlnum_generations: { enumerable: true, get: function () { return 0; } }, 139 | gmlnum_objects_in_generation: { enumerable: true, get: function () { var temp = []; return temp; } }, 140 | }); 141 | 142 | return resobj; 143 | } 144 | 145 | function gc_target_frame_time(_time) 146 | { 147 | ErrorFunction("gc_target_frame_time"); 148 | } 149 | 150 | function gc_get_target_frame_time() 151 | { 152 | ErrorFunction("gc_get_target_frame_time"); 153 | return 0; 154 | } -------------------------------------------------------------------------------- /scripts/physics/yyPhysicsDebugRender.js: -------------------------------------------------------------------------------- 1 | // ********************************************************************************************************************** 2 | // 3 | // Copyright (c)2011, YoYo Games Ltd. All Rights reserved. 4 | // 5 | // File: yyPhysicsDebugRender.js 6 | // Created: 16/02/2011 7 | // Author: Mike 8 | // Project: HTML5 9 | // Description: 10 | // 11 | // ********************************************************************************************************************** 12 | // @if feature("physics") 13 | 14 | // ############################################################################################# 15 | /// Function: 16 | /// 17 | // ############################################################################################# 18 | function translateBox2DColour(color) 19 | { 20 | return (((color.r * 255) & 0xff) << 0) | 21 | (((color.g * 255) & 0xff) << 8) | 22 | (((color.b * 255) & 0xff) << 16); 23 | } 24 | 25 | // ############################################################################################# 26 | /// Function: 27 | /// 28 | // ############################################################################################# 29 | /** @this {yyPhysicsDebugDraw} */ 30 | function yyBox2DDrawPolygon(vertices, vertexCount, color) { 31 | 32 | draw_set_color(translateBox2DColour(color)); 33 | 34 | var scale = 1.0 / this.m_pWorld.m_pixelToMetreScale; 35 | for (var n = 0; n < vertexCount; ++n) 36 | { 37 | var posA = vertices[n]; 38 | var posB = vertices[(n + 1) % vertexCount]; 39 | draw_line(posA.x * scale, posA.y * scale, posB.x * scale, posB.y * scale); 40 | } 41 | } 42 | 43 | // ############################################################################################# 44 | /// Function: 45 | /// 46 | // ############################################################################################# 47 | /** @this {yyPhysicsDebugDraw} */ 48 | function yyBox2DDrawSolidPolygon(vertices, vertexCount, color) { 49 | 50 | draw_set_color(translateBox2DColour(color)); 51 | 52 | // Draw a triangle fan in pieces 53 | var scale = 1.0 / this.m_pWorld.m_pixelToMetreScale; 54 | 55 | var posA = vertices[0]; 56 | for (var n = 2; n < vertexCount; ++n) 57 | { 58 | var posB = vertices[n - 1]; 59 | var posC = vertices[n]; 60 | draw_triangle( 61 | posA.x * scale, 62 | posA.y * scale, 63 | posB.x * scale, 64 | posB.y * scale, 65 | posC.x * scale, 66 | posC.y * scale, 67 | false); 68 | } 69 | } 70 | 71 | // ############################################################################################# 72 | /// Function: 73 | /// 74 | // ############################################################################################# 75 | /** @this {yyPhysicsDebugDraw} */ 76 | function yyBox2DDrawCircle(center, radius, color) { 77 | 78 | draw_set_color(translateBox2DColour(color)); 79 | 80 | var scale = 1.0 / this.m_pWorld.m_pixelToMetreScale; 81 | draw_ellipse((center.x - radius) * scale, 82 | (center.y - radius) * scale, 83 | (center.x + radius) * scale, 84 | (center.y + radius) * scale, 85 | true); 86 | } 87 | 88 | // ############################################################################################# 89 | /// Function: 90 | /// 91 | // ############################################################################################# 92 | /** @this {yyPhysicsDebugDraw} */ 93 | function yyBox2DDrawSolidCircle(center, radius, axis, color) { 94 | 95 | draw_set_color(translateBox2DColour(color)); 96 | 97 | var scale = 1.0 / this.m_pWorld.m_pixelToMetreScale; 98 | draw_ellipse((center.x - radius) * scale, 99 | (center.y - radius) * scale, 100 | (center.x + radius) * scale, 101 | (center.y + radius) * scale, 102 | false); 103 | } 104 | 105 | // ############################################################################################# 106 | /// Function: 107 | /// 108 | // ############################################################################################# 109 | /** @this {yyPhysicsDebugDraw} */ 110 | function yyBox2DDrawSegment(p1, p2, color) { 111 | 112 | draw_set_color(translateBox2DColour(color)); 113 | 114 | var scale = 1.0 / this.m_pWorld.m_pixelToMetreScale; 115 | draw_line(p1.x * scale, p1.y * scale, p2.x * scale, p2.y * scale); 116 | } 117 | 118 | // ############################################################################################# 119 | /// Function: 120 | /// 121 | // ############################################################################################# 122 | /** @this {yyPhysicsDebugDraw} */ 123 | function yyBox2DDrawTransform(xf) { 124 | 125 | var k_axisScale = 0.4; 126 | var scale = 1.0 / this.m_pWorld.m_pixelToMetreScale; 127 | 128 | var p1 = xf.p, 129 | p2 = new yyBox2D.Vec2(); 130 | p2.x = p1.x + k_axisScale * xf.q.GetXAxis().x; 131 | p2.y = p1.y + k_axisScale * xf.q.GetXAxis().y; 132 | draw_set_color(clRed); 133 | draw_line(p1.x * scale, p1.y * scale, p2.x * scale, p2.y * scale); 134 | 135 | p2.x = p1.x + k_axisScale * xf.q.GetYAxis().x; 136 | p2.y = p1.y + k_axisScale * xf.q.GetYAxis().y; 137 | draw_set_color(clBlue); 138 | draw_line(p1.x * scale, p1.y * scale, p2.x * scale, p2.y * scale); 139 | } 140 | 141 | // ############################################################################################# 142 | /// Function: 143 | /// 144 | // ############################################################################################# 145 | /** @this {yyPhysicsDebugDraw} */ 146 | function yyBox2DDrawParticles(centers, radius, colors, count) { 147 | 148 | for (var n = 0; n < count; n++) 149 | { 150 | var scale = 1.0 / this.m_pWorld.m_pixelToMetreScale; 151 | 152 | draw_set_color(((colors[n].r & 0xff) << 0) | ((colors[n].g & 0xff) << 8) | ((colors[n].b & 0xff) << 16)); 153 | draw_ellipse( 154 | (centers[n].x - radius) * scale, 155 | (centers[n].y - radius) * scale, 156 | (centers[n].x + radius) * scale, 157 | (centers[n].y + radius) * scale, 158 | true); 159 | } 160 | } 161 | // @endif 162 | -------------------------------------------------------------------------------- /scripts/sound/effects/EQ.js: -------------------------------------------------------------------------------- 1 | // @if feature("audio_effects") 2 | function EQEffectStruct(_params) { 3 | AudioEffectStruct.call(this, AudioEffect.Type.EQ); 4 | Object.setPrototypeOf(this, AudioEffectStruct.prototype); 5 | 6 | this.initParams(_params); 7 | 8 | const paramsWereGiven = (_params !== undefined); 9 | 10 | if (paramsWereGiven === false) { 11 | _params = {}; 12 | } 13 | 14 | this.locut = new HPF2EffectStruct(_params.gmllocut); 15 | this.loshelf = new LoShelfEffectStruct(_params.gmlloshelf); 16 | this.eq1 = new PeakEQEffectStruct(_params.gmleq1); 17 | this.eq2 = new PeakEQEffectStruct(_params.gmleq2); 18 | this.eq3 = new PeakEQEffectStruct(_params.gmleq3); 19 | this.eq4 = new PeakEQEffectStruct(_params.gmleq4); 20 | this.hishelf = new HiShelfEffectStruct(_params.gmlhishelf); 21 | this.hicut = new LPF2EffectStruct(_params.gmlhicut); 22 | 23 | if (_params.gmllocut === undefined) { 24 | this.locut.gmlcutoff = 10; 25 | this.locut.gmlq = 1; 26 | } 27 | 28 | if (_params.gmlloshelf === undefined) { 29 | this.loshelf.gmlfreq = 200; 30 | this.loshelf.gmlgain = (paramsWereGiven === true) ? db_to_lin(0) : db_to_lin(12) 31 | } 32 | 33 | if (_params.gmleq1 === undefined) { 34 | this.eq1.gmlfreq = 500; 35 | this.eq1.gmlgain = (paramsWereGiven === true) ? db_to_lin(0) : db_to_lin(-24); 36 | } 37 | 38 | if (_params.gmleq2 === undefined) { 39 | this.eq2.gmlfreq = 1000; 40 | this.eq2.gmlgain = db_to_lin(0); 41 | } 42 | 43 | if (_params.gmleq3 === undefined) { 44 | this.eq3.gmlfreq = 2000; 45 | this.eq3.gmlgain = db_to_lin(0); 46 | } 47 | 48 | if (_params.gmleq4 === undefined) { 49 | this.eq4.gmlfreq = 3000; 50 | this.eq4.gmlgain = db_to_lin(0); 51 | } 52 | 53 | if (_params.gmlhishelf === undefined) { 54 | this.hishelf.gmlfreq = 5000; 55 | this.hishelf.gmlgain = (paramsWereGiven === true) ? db_to_lin(0) : db_to_lin(18); 56 | } 57 | 58 | if (_params.gmlhicut === undefined) { 59 | this.hicut.gmlcutoff = LPF2EffectStruct.ParamDescriptors[LPF2EffectStruct.Index.Cutoff].maxValue; 60 | this.hicut.gmlq = 1; 61 | } 62 | 63 | Object.defineProperties(this, { 64 | gmllocut: { 65 | enumerable: true, 66 | get: () => { 67 | return this.locut; 68 | }, 69 | set: (_unused) => {} 70 | }, 71 | gmlloshelf: { 72 | enumerable: true, 73 | get: () => { 74 | return this.loshelf; 75 | }, 76 | set: (_unused) => {} 77 | }, 78 | gmleq1: { 79 | enumerable: true, 80 | get: () => { 81 | return this.eq1; 82 | }, 83 | set: (_unused) => {} 84 | }, 85 | gmleq2: { 86 | enumerable: true, 87 | get: () => { 88 | return this.eq2; 89 | }, 90 | set: (_unused) => {} 91 | }, 92 | gmleq3: { 93 | enumerable: true, 94 | get: () => { 95 | return this.eq3; 96 | }, 97 | set: (_unused) => {} 98 | }, 99 | gmleq4: { 100 | enumerable: true, 101 | get: () => { 102 | return this.eq4; 103 | }, 104 | set: (_unused) => {} 105 | }, 106 | gmlhishelf: { 107 | enumerable: true, 108 | get: () => { 109 | return this.hishelf; 110 | }, 111 | set: (_unused) => {} 112 | }, 113 | gmlhicut: { 114 | enumerable: true, 115 | get: () => { 116 | return this.hicut; 117 | }, 118 | set: (_unused) => {} 119 | } 120 | }); 121 | 122 | this.addInstance = function() { 123 | const maxChannels = g_WebAudioContext.destination.channelCount; 124 | 125 | const inputNode = new AudioWorkletNode(g_WebAudioContext, "eq-input", { 126 | numberOfInputs: 1, 127 | numberOfOutputs: 2, 128 | outputChannelCount: [maxChannels, maxChannels], 129 | channelCount: maxChannels, 130 | channelCountMode: "explicit" 131 | }); 132 | 133 | const locutNode = this.locut.addInstance(); 134 | const loshelfNode = this.loshelf.addInstance(); 135 | const eq1Node = this.eq1.addInstance(); 136 | const eq2Node = this.eq2.addInstance(); 137 | const eq3Node = this.eq3.addInstance(); 138 | const eq4Node = this.eq4.addInstance(); 139 | const hishelfNode = this.hishelf.addInstance(); 140 | const hicutNode = this.hicut.addInstance(); 141 | 142 | const outputNode = new AudioWorkletNode(g_WebAudioContext, "eq-output", { 143 | numberOfInputs: 2, 144 | numberOfOutputs: 1, 145 | outputChannelCount: [maxChannels], 146 | channelCount: maxChannels, 147 | channelCountMode: "explicit" 148 | }); 149 | 150 | this.nodes.push(outputNode); 151 | 152 | inputNode.connect(outputNode, 1, 1); 153 | inputNode.connect(locutNode.input, 0, 0); 154 | locutNode.output.connect(loshelfNode.input, 0, 0); 155 | loshelfNode.output.connect(eq1Node.input, 0, 0); 156 | eq1Node.output.connect(eq2Node.input, 0, 0); 157 | eq2Node.output.connect(eq3Node.input, 0, 0); 158 | eq3Node.output.connect(eq4Node.input, 0, 0); 159 | eq4Node.output.connect(hishelfNode.input, 0, 0); 160 | hishelfNode.output.connect(hicutNode.input, 0, 0); 161 | hicutNode.output.connect(outputNode, 0, 0); 162 | 163 | g_WorkletNodeManager.nodes.push({ 164 | struct: new WeakRef(this), 165 | node: inputNode 166 | }); 167 | 168 | g_WorkletNodeManager.nodes.push({ 169 | struct: new WeakRef(this), 170 | node: outputNode 171 | }); 172 | 173 | const ret = { input: inputNode, output: outputNode }; 174 | return ret; 175 | }; 176 | } 177 | 178 | EQEffectStruct.Index = { 179 | Bypass: 0 180 | }; 181 | 182 | EQEffectStruct.ParamDescriptors = [ 183 | { name: "bypass", integer: true, defaultValue: 0, minValue: 0, maxValue: 1 } 184 | ]; 185 | // @endif 186 | -------------------------------------------------------------------------------- /scripts/SWF/yySWFTimeline.js: -------------------------------------------------------------------------------- 1 | // ********************************************************************************************************************** 2 | // 3 | // Copyright (c)2011, YoYo Games Ltd. All Rights reserved. 4 | // 5 | // File: yySWFTimeline.js 6 | // Created: 16/02/2011 7 | // Author: Mike 8 | // Project: HTML5 9 | // Description: 10 | // 11 | // ********************************************************************************************************************** 12 | 13 | // @if feature("swf") 14 | // convert from twips to pixels (each twip is 20 pixels) 15 | var g_SWF_twipscale = 1.0 / 20.0; 16 | 17 | // ############################################################################################# 18 | /// Function: 19 | /// Parse shape data for this shape 20 | /// 21 | // ############################################################################################# 22 | /** @constructor */ 23 | function yySWFTimeline() { 24 | 25 | this.FrameRate = 0; 26 | this.numFrames = 0; 27 | this.minX = 0; 28 | this.maxX = 0; 29 | this.minY = 0; 30 | this.maxY = 0; 31 | this.collisionMaskHeader = null; 32 | }; 33 | 34 | 35 | // ############################################################################################# 36 | /// Function: 37 | /// Parse shape data for this shape 38 | /// 39 | // ############################################################################################# 40 | yySWFTimeline.prototype.BuildTimelineData = function (_dataView, _byteOffset, _littleEndian) { 41 | 42 | this.FrameRate = _dataView.getInt32(_byteOffset, _littleEndian); 43 | _byteOffset+=4; 44 | this.numFrames = _dataView.getInt32(_byteOffset, _littleEndian); 45 | _byteOffset+=4; 46 | this.minX = _dataView.getFloat32(_byteOffset, _littleEndian) * g_SWF_twipscale; 47 | _byteOffset+=4; 48 | this.maxX = _dataView.getFloat32(_byteOffset, _littleEndian) * g_SWF_twipscale; 49 | _byteOffset+=4; 50 | this.minY = _dataView.getFloat32(_byteOffset, _littleEndian) * g_SWF_twipscale; 51 | _byteOffset+=4; 52 | this.maxY = _dataView.getFloat32(_byteOffset, _littleEndian) * g_SWF_twipscale; 53 | _byteOffset+=4; 54 | 55 | // Read out collision masks header 56 | var numCollisionMasks, maskWidth, maskHeight; 57 | numCollisionMasks = _dataView.getInt32(_byteOffset, _littleEndian); 58 | _byteOffset+=4; 59 | maskWidth = _dataView.getInt32(_byteOffset, _littleEndian); 60 | _byteOffset+=4; 61 | maskHeight = _dataView.getInt32(_byteOffset, _littleEndian); 62 | _byteOffset+=4; 63 | this.collisionMaskHeader = { 64 | numCollisionMasks: numCollisionMasks, 65 | maskWidth: maskWidth, 66 | maskHeight: maskHeight 67 | }; 68 | 69 | // Read out the frames 70 | this.Frames = []; 71 | for (var i = 0; i < this.numFrames; i++) 72 | { 73 | var pFrame = {}; 74 | this.Frames.push(pFrame); 75 | 76 | pFrame.numObjects = _dataView.getInt32(_byteOffset, _littleEndian); 77 | _byteOffset+=4; 78 | pFrame.minX = _dataView.getFloat32(_byteOffset, _littleEndian) * g_SWF_twipscale; 79 | _byteOffset+=4; 80 | pFrame.maxX = _dataView.getFloat32(_byteOffset, _littleEndian) * g_SWF_twipscale; 81 | _byteOffset+=4; 82 | pFrame.minY = _dataView.getFloat32(_byteOffset, _littleEndian) * g_SWF_twipscale; 83 | _byteOffset+=4; 84 | pFrame.maxY = _dataView.getFloat32(_byteOffset, _littleEndian) * g_SWF_twipscale; 85 | _byteOffset+=4; 86 | 87 | pFrame.Objects = []; 88 | for (var j = 0; j < pFrame.numObjects; j++) 89 | { 90 | var pObject = {}; 91 | pFrame.Objects.push(pObject); 92 | 93 | pObject.ID = _dataView.getInt32(_byteOffset, _littleEndian); 94 | _byteOffset+=4; 95 | 96 | pObject.index = _dataView.getInt32(_byteOffset, _littleEndian); 97 | _byteOffset+=4; 98 | 99 | pObject.depth = _dataView.getInt32(_byteOffset, _littleEndian); 100 | _byteOffset+=4; 101 | 102 | pObject.clipDepth = _dataView.getInt32(_byteOffset, _littleEndian); 103 | _byteOffset+=4; 104 | 105 | // The format is RGBA in the file - will need to rearrange 106 | var colmul = []; 107 | for (var k = 0; k < 4; k++) 108 | { 109 | colmul[k] = _dataView.getInt32(_byteOffset, _littleEndian); 110 | _byteOffset+=4; 111 | } 112 | 113 | var coladd = []; 114 | for (var k = 0; k < 4; k++) 115 | { 116 | coladd[k] = _dataView.getInt32(_byteOffset, _littleEndian); 117 | _byteOffset+=4; 118 | } 119 | 120 | // WIN_CLASSIC format is BGRA, rather than RGBA 121 | pObject.colTransMul = []; 122 | pObject.colTransAdd = []; 123 | pObject.colTransAddZeroAlpha = []; 124 | 125 | pObject.colTransMul[0] = colmul[2]; 126 | pObject.colTransAdd[0] = coladd[2]; 127 | pObject.colTransAddZeroAlpha[0] = 0; 128 | 129 | pObject.colTransMul[1] = colmul[1]; 130 | pObject.colTransAdd[1] = coladd[1]; 131 | pObject.colTransAddZeroAlpha[1] = coladd[1]; 132 | 133 | pObject.colTransMul[2] = colmul[0]; 134 | pObject.colTransAdd[2] = coladd[0]; 135 | pObject.colTransAddZeroAlpha[2] = coladd[0]; 136 | 137 | pObject.colTransMul[3] = colmul[3]; 138 | pObject.colTransAdd[3] = coladd[3]; 139 | pObject.colTransAddZeroAlpha[3] = coladd[3]; 140 | 141 | pObject.minX = _dataView.getFloat32(_byteOffset, _littleEndian) * g_SWF_twipscale; 142 | _byteOffset+=4; 143 | pObject.maxX = _dataView.getFloat32(_byteOffset, _littleEndian) * g_SWF_twipscale; 144 | _byteOffset+=4; 145 | pObject.minY = _dataView.getFloat32(_byteOffset, _littleEndian) * g_SWF_twipscale; 146 | _byteOffset+=4; 147 | pObject.maxY = _dataView.getFloat32(_byteOffset, _littleEndian) * g_SWF_twipscale; 148 | _byteOffset+=4; 149 | 150 | var transMat = []; 151 | for (var k = 0; k < 9; k++) 152 | { 153 | transMat[k] = _dataView.getFloat32(_byteOffset, _littleEndian); 154 | _byteOffset+=4; 155 | } 156 | 157 | // Convert 3x3 to 4x4 matrix 158 | pObject.transMat = new Matrix(); 159 | pObject.transMat.m[_11] = transMat[0]; 160 | pObject.transMat.m[_21] = transMat[1]; 161 | pObject.transMat.m[_41] = transMat[2]; 162 | 163 | pObject.transMat.m[_12] = transMat[3]; 164 | pObject.transMat.m[_22] = transMat[4]; 165 | pObject.transMat.m[_42] = transMat[5]; 166 | } 167 | } 168 | return _byteOffset; 169 | }; 170 | // @endif -------------------------------------------------------------------------------- /scripts/functions/Function_Timeline.js: -------------------------------------------------------------------------------- 1 | 2 | // ********************************************************************************************************************** 3 | // 4 | // Copyright (c)2011, YoYo Games Ltd. All Rights reserved. 5 | // 6 | // File: Function_Timeline.js 7 | // Created: 27/05/2011 8 | // Author: Mike 9 | // Project: HTML5 10 | // Description: 11 | // 12 | // Date Version BY Comment 13 | // ---------------------------------------------------------------------------------------------------------------------- 14 | // 27/05/2011 15 | // 16 | // ********************************************************************************************************************** 17 | 18 | 19 | 20 | // ############################################################################################# 21 | /// Function: 22 | /// Returns whether a time line with the given index exists. 23 | /// 24 | /// 25 | /// In: 26 | /// Out: 27 | /// 28 | /// 29 | // ############################################################################################# 30 | function timeline_exists(_ind) 31 | { 32 | if (_ind === undefined) return false; 33 | if (g_pTimelineManager.Get(yyGetInt32(_ind)) != undefined) { 34 | return true; 35 | } 36 | return false; 37 | } 38 | 39 | // ############################################################################################# 40 | /// Function: 41 | /// Returns the name of the time line with the given index. 42 | /// 43 | /// 44 | /// In: 45 | /// Out: 46 | /// 47 | /// 48 | // ############################################################################################# 49 | function timeline_get_name(_ind) 50 | { 51 | var tl = g_pTimelineManager.Get(yyGetInt32(_ind)); 52 | if (tl != undefined) { 53 | return tl.pName; 54 | } 55 | return ""; 56 | } 57 | 58 | function timeline_name(_ind) { return timeline_get_name(_ind); } 59 | 60 | // ############################################################################################# 61 | /// Function: 62 | /// Adds a new time line. It returns the index of the time line. 63 | /// 64 | /// 65 | /// Out: 66 | /// ID of new timeline 67 | /// 68 | // ############################################################################################# 69 | function timeline_add() 70 | { 71 | return g_pTimelineManager.AddNew(); 72 | } 73 | 74 | // ############################################################################################# 75 | /// Function: 76 | /// Deletes the time line with the given index. Make sure no instances uses the 77 | /// time line in any room. 78 | /// 79 | /// 80 | /// In: 81 | /// Out: 82 | /// 83 | /// 84 | // ############################################################################################# 85 | function timeline_delete(_ind) 86 | { 87 | g_pTimelineManager.Delete(yyGetInt32(_ind)); 88 | } 89 | 90 | // ############################################################################################# 91 | /// Function: 92 | /// Clears the time line with the given index removing all its moments. 93 | /// Make sure no instances uses the time line at the moment. 94 | /// 95 | /// 96 | /// In: 97 | /// Out: 98 | /// 99 | /// 100 | // ############################################################################################# 101 | function timeline_clear(_ind) 102 | { 103 | g_pTimelineManager.Clear(yyGetInt32(_ind)); 104 | } 105 | 106 | // ############################################################################################# 107 | /// Function: 108 | /// Adds a code action to the time line at moment step. codestr contains the code 109 | /// for the actions. If the step does not exist it is created. So you can add 110 | /// multiple code actions for the same moment. 111 | /// 112 | /// 113 | /// In: 114 | /// 115 | /// 116 | /// Out: 117 | /// 118 | /// 119 | // ############################################################################################# 120 | function timeline_moment_add(_ind,_step,_codestr) 121 | { 122 | //g_pTimelineManager.AddEvent(_ind, _step, _codestr); 123 | ErrorFunction("timeline_moment_add()"); 124 | } 125 | 126 | function timeline_moment_add_script(_ind,_step,_script) 127 | { 128 | // get the script function 129 | switch( typeof _script) { 130 | case "number": 131 | case "function": 132 | var func = typeof _script == "number" ? g_pGMFile.Scripts[yyGetInt32(_script) - 100000] : _script; 133 | g_pTimelineManager.AddEvent(yyGetInt32(_ind), yyGetInt32(_step), func ); 134 | break; 135 | default: 136 | break; 137 | } 138 | } 139 | 140 | function timeline_size(_ind) 141 | { 142 | var tl = g_pTimelineManager.Get(yyGetInt32(_ind)); 143 | var ret = 0; 144 | if (tl != undefined) { 145 | ret = tl.Events.count; 146 | } // end if 147 | return ret; 148 | } 149 | 150 | function timeline_max_moment(_ind) 151 | { 152 | var tl = g_pTimelineManager.Get(yyGetInt32(_ind)); 153 | var ret = 0; 154 | if (tl != undefined) { 155 | ret = tl.GetLast(); 156 | } // end if 157 | return ret; 158 | } 159 | 160 | // ############################################################################################# 161 | /// Function: 162 | /// You can use this function to clear all the actions for a particular moment. 163 | /// 164 | /// 165 | /// In: 166 | /// 167 | /// Out: 168 | /// 169 | /// 170 | // ############################################################################################# 171 | function timeline_moment_clear(_ind,_step) 172 | { 173 | //ErrorFunction("timeline_moment_clear()"); 174 | g_pTimelineManager.ClearEvent(yyGetInt32(_ind), yyGetInt32(_step)); 175 | } 176 | 177 | -------------------------------------------------------------------------------- /scripts/sound/AudioBus.js: -------------------------------------------------------------------------------- 1 | // @if feature("audio") 2 | function AudioBus() { 3 | // GML object props 4 | this.__type = "[AudioBus]"; 5 | this.__yyIsGMLObject = true; 6 | 7 | this.inputNode = g_WorkletNodeManager.createBusInput(this); 8 | this.outputNode = g_WorkletNodeManager.createBusOutput(this); 9 | 10 | this.inputNode.connect(this.outputNode, 0, 0); // Initial effect chain connection 11 | this.inputNode.connect(this.outputNode, 1, 1); // Bypass connection 12 | 13 | this.bypass = false; 14 | this.gain = 1.0; 15 | this.effects = Array(AudioBus.NUM_EFFECT_SLOTS).fill(undefined); 16 | this.nodes = Array(AudioBus.NUM_EFFECT_SLOTS).fill(undefined); 17 | 18 | this.proxy = new Proxy(this.effects, { 19 | set: (_target, _property, _value, _receiver) => { 20 | const propAsInt = parseInt(_property); 21 | 22 | if (AudioBus.isNodeIndex(propAsInt)) 23 | this.handleConnections(propAsInt, this.handleValue(_value)); 24 | 25 | _target[_property] = _value; 26 | } 27 | }); 28 | 29 | // Define user-facing properties 30 | Object.defineProperties(this, { 31 | gmlbypass: { 32 | enumerable: true, 33 | get: () => { 34 | return this.bypass; 35 | }, 36 | set: (_state) => { 37 | this.bypass = yyGetBool(_state); 38 | 39 | const bypass = this.inputNode.parameters.get("bypass"); 40 | bypass.value = this.bypass; 41 | } 42 | }, 43 | gmlgain: { 44 | enumerable: true, 45 | get: () => { 46 | return this.gain; 47 | }, 48 | set: (_gain) => { 49 | this.gain = max(0.0, _gain); 50 | 51 | const gain = this.outputNode.parameters.get("gain"); 52 | gain.value = this.gain; 53 | } 54 | }, 55 | gmleffects: { 56 | enumerable: true, 57 | get: () => { 58 | return this.proxy; 59 | }, 60 | set: (_effects) => {} 61 | } 62 | }); 63 | } 64 | 65 | AudioBus.NUM_EFFECT_SLOTS = 8; 66 | 67 | AudioBus.prototype.connectInput = function(_source, _outputIndex, _inputIndex) 68 | { 69 | _source.connect(this.inputNode, _outputIndex, _inputIndex); 70 | }; 71 | 72 | AudioBus.prototype.connectOutput = function(_destination, _outputIndex, _inputIndex) 73 | { 74 | this.outputNode.connect(_destination, _outputIndex, _inputIndex); 75 | }; 76 | 77 | AudioBus.prototype.findNextNode = function(_idx) 78 | { 79 | const nodes = this.nodes.slice(_idx + 1, AudioBus.NUM_EFFECT_SLOTS); 80 | const nextNode = nodes.find((_node) => _node !== undefined); 81 | 82 | return (nextNode !== undefined) ? nextNode.input : this.outputNode; 83 | }; 84 | 85 | AudioBus.prototype.findPrevNode = function(_idx) 86 | { 87 | const nodes = this.nodes.slice(0, _idx); 88 | const prevNode = nodes.slice().reverse().find((_node) => _node !== undefined); 89 | 90 | return (prevNode !== undefined) ? prevNode.output : this.inputNode; 91 | }; 92 | 93 | AudioBus.prototype.handleConnections = function(_idx, _newNodes) 94 | { 95 | const currentNodes = this.nodes[_idx]; 96 | 97 | if (currentNodes === undefined && _newNodes === undefined) 98 | return; // No need to change anything 99 | 100 | const prevNode = this.findPrevNode(_idx); 101 | const nextNode = this.findNextNode(_idx); 102 | 103 | // Disconnect the previous node 104 | if (currentNodes !== undefined) 105 | { 106 | prevNode.disconnect(currentNodes.input); 107 | 108 | currentNodes.output.disconnect(); 109 | this.effects[_idx].removeNode(currentNodes.output); 110 | } 111 | else 112 | { 113 | prevNode.disconnect(nextNode, 0, 0); 114 | } 115 | 116 | // Reconnect the previous node (and any new node) 117 | if (_newNodes === undefined) 118 | { 119 | prevNode.connect(nextNode, 0, 0); 120 | } 121 | else 122 | { 123 | prevNode.connect(_newNodes.input, 0, 0); 124 | _newNodes.output.connect(nextNode, 0, 0); 125 | } 126 | 127 | this.nodes[_idx] = _newNodes; 128 | }; 129 | 130 | AudioBus.prototype.handleValue = function(_value) 131 | { 132 | if (_value instanceof AudioEffectStruct) { 133 | return _value.addInstance(); 134 | } 135 | 136 | if (_value === undefined) { 137 | return _value; 138 | } 139 | 140 | throw new Error("Value must be Struct.AudioEffect or undefined"); 141 | }; 142 | 143 | AudioBus.isNodeIndex = function(_prop) 144 | { 145 | if (_prop >= 0 && _prop < AudioBus.NUM_EFFECT_SLOTS) 146 | return true; 147 | 148 | return false; 149 | }; 150 | 151 | AudioBus.prototype.getParamDescriptors = function() { 152 | return AudioBus.ParamDescriptors; 153 | }; 154 | 155 | AudioBus.prototype.getParamDescriptor = function(_idx) { 156 | return AudioBus.ParamDescriptors[_idx]; 157 | }; 158 | 159 | AudioBus.ParamDescriptors = [ 160 | { name: "bypass", integer: true, defaultValue: 0, minValue: 0, maxValue: 1 }, 161 | { name: "gain", integer: false, defaultValue: 1, minValue: 0, maxValue: Number.MAX_VALUE } 162 | ]; 163 | 164 | function DummyAudioBus() { 165 | this.outputNode = Audio_CreateGainNode(g_WebAudioContext); 166 | 167 | this.bypass = false; 168 | this.gain = 1.0; 169 | this.effects = Array(AudioBus.NUM_EFFECT_SLOTS).fill(undefined); 170 | 171 | this.proxy = new Proxy(this.effects, { 172 | set: (_target, _property, _value, _receiver) => { 173 | const propAsInt = parseInt(_property); 174 | 175 | if (AudioBus.isNodeIndex(propAsInt)) 176 | _value = this.handleValue(_value); 177 | 178 | _target[_property] = _value; 179 | } 180 | }); 181 | 182 | Object.defineProperties(this, { 183 | gmlbypass: { 184 | enumerable: true, 185 | get: () => { 186 | return this.bypass; 187 | }, 188 | set: (_state) => { 189 | this.bypass = yyGetBool(_state); 190 | } 191 | }, 192 | gmlgain: { 193 | enumerable: true, 194 | get: () => { 195 | return this.gain; 196 | }, 197 | set: (_gain) => { 198 | this.gain = max(0.0, _gain); 199 | 200 | const gain = this.outputNode.gain; 201 | gain.setTargetAtTime(this.gain, 0, AudioEffect.PARAM_TIME_CONSTANT); 202 | } 203 | }, 204 | gmleffects: { 205 | enumerable: true, 206 | get: () => { 207 | return this.proxy; 208 | }, 209 | set: (_effects) => {} 210 | } 211 | }); 212 | } 213 | 214 | DummyAudioBus.prototype.connectInput = function(_source, _outputIndex, _inputIndex) 215 | { 216 | _source.connect(this.outputNode); 217 | }; 218 | 219 | DummyAudioBus.prototype.connectOutput = function(_destination, _outputIndex, _inputIndex) 220 | { 221 | this.outputNode.connect(_destination, _outputIndex, _inputIndex); 222 | }; 223 | 224 | DummyAudioBus.prototype.handleValue = function(_value) 225 | { 226 | if (_value instanceof AudioEffectStruct) 227 | return _value; 228 | 229 | if (_value === undefined) 230 | return _value; 231 | 232 | throw new Error("Value must be Struct.AudioEffect or undefined"); 233 | }; 234 | // @endif audio 235 | -------------------------------------------------------------------------------- /scripts/functions/Function_AnimCurve.js: -------------------------------------------------------------------------------- 1 | // ********************************************************************************************************************** 2 | // 3 | // Copyright (c)2011, YoYo Games Ltd. All Rights reserved. 4 | // 5 | // File: Function_AnimCurve.js 6 | // Created: 11/10/2019 7 | // Author: Luke 8 | // Project: HTML5 9 | // Description: 10 | // 11 | // Date Version BY Comment 12 | // ---------------------------------------------------------------------------------------------------------------------- 13 | // 11/10/2019 V1.0 MJD 1st version 14 | // 15 | // ********************************************************************************************************************** 16 | 17 | function GetCurveFromRValue(_val) 18 | { 19 | var pCurve = null; 20 | if (typeof (_val) == "object") 21 | //if (_val instanceof yyAnimCurve) 22 | { 23 | pCurve = _val; 24 | } 25 | else 26 | { 27 | var curveID = yyGetInt32(_val); 28 | pCurve = g_pAnimCurveManager.GetCurveFromID(curveID); 29 | } 30 | 31 | return pCurve; 32 | } 33 | 34 | function animcurve_get(_curveID) 35 | { 36 | if (arguments.length != 1) 37 | { 38 | yyError("animcurve_get() - wrong number of arguments"); 39 | return; 40 | } 41 | 42 | var pCurve = GetCurveFromRValue(_curveID); 43 | if (pCurve == null) 44 | { 45 | yyError("animcurve_get() - specified curve not valid"); 46 | } 47 | else 48 | { 49 | return pCurve; 50 | } 51 | 52 | return -1; 53 | } 54 | 55 | function animcurve_get_channel_index(_curveIdOrCurveObject, _channelName) 56 | { 57 | var result = -1; 58 | if (arguments.length != 2) 59 | { 60 | yyError("animcurve_get_channel_index() - wrong number of arguments"); 61 | return result; 62 | } 63 | 64 | var pCurve = GetCurveFromRValue(_curveIdOrCurveObject); 65 | if (pCurve == null) 66 | { 67 | yyError("animcurve_get_channel_index() - specified curve not valid"); 68 | return result; 69 | } 70 | 71 | var name = yyGetString(_channelName); // we don't need to free this 72 | for (var i = 0; i < pCurve.m_numChannels; i++) 73 | { 74 | var pChan = pCurve.m_channels[i]; 75 | if ((pChan != null) && (pChan.m_name != null) && pChan.m_name == name) 76 | { 77 | result = i; 78 | break; 79 | } 80 | } 81 | return result; 82 | } 83 | 84 | function animcurve_get_channel(_curveIdOrCurveObject, _channelIndexOrName) 85 | { 86 | var result = -1; 87 | 88 | if (arguments.length != 2) 89 | { 90 | yyError("animcurve_get_channel() - wrong number of arguments"); 91 | return; 92 | } 93 | 94 | if (typeof(_curveIdOrCurveObject) != "number" && typeof(_curveIdOrCurveObject) != "object") 95 | { 96 | yyError("animcurve_get_channel() - first parameter must be either curve ID or curve object"); 97 | return; 98 | } 99 | 100 | if (typeof(_channelIndexOrName) != "number" && typeof(_channelIndexOrName) != "string") 101 | { 102 | yyError("animcurve_get_channel() - second parameter must be either channel index or channel name"); 103 | return; 104 | } 105 | 106 | var pCurve = GetCurveFromRValue(_curveIdOrCurveObject); 107 | 108 | if (pCurve != null) 109 | { 110 | if (typeof(_channelIndexOrName) == "number") 111 | { 112 | var channelindex = yyGetInt32(_channelIndexOrName); 113 | if ((channelindex < 0) || (channelindex >= pCurve.m_numChannels)) 114 | { 115 | yyError("animcurve_get_channel() - specified channel index out of range"); 116 | return; 117 | } 118 | 119 | if (pCurve.m_channels[channelindex] == null) 120 | { 121 | yyError("animcurve_get_channel() - specified channel is invalid"); 122 | return; 123 | } 124 | 125 | result = pCurve.m_channels[channelindex]; 126 | } 127 | else 128 | { 129 | var name = yyGetString(_channelIndexOrName); // we don't need to free this 130 | 131 | // Pretty low-tech 132 | for (var i = 0; i < pCurve.m_numChannels; i++) 133 | { 134 | var pChan = pCurve.m_channels[i]; 135 | if ((pChan != null) && (pChan.m_name != null) && pChan.m_name == name) 136 | { 137 | result = pChan; 138 | break; 139 | } 140 | } 141 | } 142 | } 143 | 144 | return result; 145 | } 146 | 147 | function animcurve_channel_evaluate(_curveObject, _value) 148 | { 149 | if (arguments.length != 2) 150 | { 151 | yyError("animcurve_channel_evaluate() - wrong number of arguments"); 152 | return; 153 | } 154 | 155 | if (_curveObject == null || !(_curveObject instanceof yyAnimCurveChannel)) 156 | { 157 | yyError("animcurve_channel_evaluate() - first parameter is not valid animation curve channel"); 158 | return; 159 | } 160 | 161 | return _curveObject.Evaluate(yyGetReal(_value)); 162 | } 163 | 164 | function animcurve_create() 165 | { 166 | var pCurve = g_pAnimCurveManager.GetNewCurve(); 167 | 168 | if (pCurve == null) 169 | { 170 | yyError("animcurve_create() - could not create new curve"); 171 | } 172 | 173 | return pCurve; 174 | } 175 | 176 | function animcurve_destroy(_curveIdOrCurveObject) 177 | { 178 | if (arguments.length != 1) 179 | { 180 | yyError("animcurve_destroy() - requires a curve ID or object"); 181 | } 182 | 183 | var pCurve = GetCurveFromRValue(_curveIdOrCurveObject); 184 | 185 | if (pCurve == null) 186 | { 187 | yyError("animcurve_destroy() - specified curve not valid"); 188 | } 189 | else if (pCurve.fromWAD == true) 190 | { 191 | yyError("animcurve_destroy() - can't delete a curve created in the IDE"); 192 | } 193 | else 194 | { 195 | g_pAnimCurveManager.FreeCurve(pCurve); 196 | } 197 | } 198 | 199 | function animcurve_exists(_curveIdOrCurveObject) 200 | { 201 | if (_curveIdOrCurveObject === undefined) return false; 202 | if (arguments.length != 1) 203 | { 204 | yyError("animcurve_exists() - requires a curve ID or struct"); 205 | } 206 | 207 | var exists = false; 208 | if (typeof (_curveIdOrCurveObject) == "object") 209 | { 210 | if (_curveIdOrCurveObject instanceof yyAnimCurve) 211 | { 212 | if (g_pAnimCurveManager.IsLiveCurve(_curveIdOrCurveObject)) 213 | { 214 | exists = true; 215 | } 216 | } 217 | } 218 | else 219 | { 220 | var curveID = yyGetInt32(_curveIdOrCurveObject); 221 | if (g_pAnimCurveManager.GetCurveFromID(curveID) != null) 222 | { 223 | exists = true; 224 | } 225 | } 226 | 227 | return exists ? 1.0 : 0.0; 228 | } 229 | 230 | function animcurve_channel_new() 231 | { 232 | return new yyAnimCurveChannel(); 233 | } 234 | 235 | function animcurve_point_new() 236 | { 237 | return new yyAnimCurvePoint(); 238 | } 239 | -------------------------------------------------------------------------------- /scripts/yyAllocate.js: -------------------------------------------------------------------------------- 1 | // ********************************************************************************************************************** 2 | // 3 | // Copyright (c)2011, YoYo Games Ltd. All Rights reserved. 4 | // 5 | // File: yyAllocate.js 6 | // Created: 15/07/2011 7 | // Author: Mike 8 | // Project: GameMaker HTML5 9 | // Description: A list which does fast allocation of a slot in an array using a stack for free entries. 10 | // 11 | // Date Version BY Comment 12 | // ---------------------------------------------------------------------------------------------------------------------- 13 | // 15/07/2011 V1.0 MJD 1st version 14 | // 15 | // ********************************************************************************************************************** 16 | 17 | 18 | // ############################################################################################# 19 | /// Function: 20 | /// Allocate an slot 21 | /// 22 | /// 23 | /// In: Number of slots to pre-allocate 24 | /// Out: 25 | /// the new class 26 | /// 27 | // ############################################################################################# 28 | /** @constructor */ 29 | function yyAllocate( _initalCount ) 30 | { 31 | var args = arguments; 32 | var argc = arguments.length; 33 | 34 | this.pool = []; 35 | this.stack = []; // allocation stack 36 | this.length = 0; // size of the ARRAY 37 | this.count = 0; // number of elements IN the array 38 | 39 | if (argc > 0) 40 | { 41 | for (var i = 0; i < argc; i++) 42 | { 43 | this.pool[i] = null; 44 | this.stack.push(i); 45 | } 46 | } 47 | } 48 | 49 | 50 | // ############################################################################################# 51 | /// Function: 52 | /// Allocate an slot 53 | /// 54 | /// 55 | /// Out: 56 | /// An object index 57 | /// 58 | // ############################################################################################# 59 | yyAllocate.prototype.Alloc = function () 60 | { 61 | var n; 62 | if (this.stack.length === 0) 63 | { 64 | n = this.pool.length; // get new index 65 | this.pool[n] = null; 66 | return n; 67 | } 68 | 69 | return this.stack.pop(); 70 | }; 71 | 72 | 73 | // ############################################################################################# 74 | /// Function: 75 | /// Add an item to the managers lists 76 | /// 77 | /// 78 | /// In: Object to add 79 | // ############################################################################################# 80 | yyAllocate.prototype.Add = function (_pItem) 81 | { 82 | var index = this.Alloc(); 83 | this.pool[index] = _pItem; 84 | this.count++; 85 | this.length = this.pool.length; 86 | return index; 87 | }; 88 | 89 | 90 | // ############################################################################################# 91 | /// Function: 92 | /// Get the item from the pool 93 | /// 94 | /// 95 | /// In: Object index to return 96 | /// Out: 97 | /// The object, or NULL if not found 98 | /// 99 | // ############################################################################################# 100 | yyAllocate.prototype.Get = function (_objind) 101 | { 102 | var pVar = null; 103 | if(_objind >= 0 && _objind < this.pool.length) 104 | { 105 | pVar = this.pool[_objind]; 106 | } 107 | if( pVar === undefined ) pVar = null; 108 | return pVar; 109 | }; 110 | 111 | 112 | 113 | // ############################################################################################# 114 | /// Function: 115 | /// Find an item in the pool, and return its index 116 | /// 117 | /// 118 | /// In: Item to remove 119 | // ############################################################################################# 120 | yyAllocate.prototype.FindItem = function (_item) 121 | { 122 | for (var l = 0; l < this.pool.length; l++) 123 | { 124 | if (this.pool[l] == _item) return l; 125 | } 126 | return -1; 127 | }; 128 | 129 | 130 | // ############################################################################################# 131 | /// Function: 132 | /// Delete an item from the pool by searching for it 133 | /// 134 | /// 135 | /// In: Item to remove 136 | // ############################################################################################# 137 | yyAllocate.prototype.DeleteItem = function (_item) { 138 | var index = this.FindItem(_item); 139 | if (index < 0) return; 140 | 141 | this.pool[index] = null; 142 | this.stack.push(index); 143 | this.count--; 144 | }; 145 | 146 | 147 | 148 | // ############################################################################################# 149 | /// Function: 150 | /// Delete an item from the pool using it's index 151 | /// 152 | /// 153 | /// In: Items index to remove 154 | // ############################################################################################# 155 | yyAllocate.prototype.DeleteIndex = function (_objind) { 156 | if (_objind < 0 || _objind >= this.pool.length) return; 157 | 158 | this.pool[_objind] = null; 159 | this.stack.push(_objind); 160 | this.count--; 161 | }; 162 | 163 | // ############################################################################################# 164 | /// Function: 165 | /// clear the list (just remake it) 166 | /// 167 | // ############################################################################################# 168 | yyAllocate.prototype.Clear = function () { 169 | this.pool = []; 170 | this.stack = []; 171 | this.count = 0; 172 | this.length = this.pool.length; 173 | }; 174 | 175 | 176 | // ############################################################################################# 177 | /// Function: 178 | /// Sets a value at the provided index 179 | /// 180 | // ############################################################################################# 181 | yyAllocate.prototype.Set = function (_index, _val) { 182 | if (_index < 0 || _index >= this.pool.length) return; 183 | this.pool[_index] = _val; 184 | }; 185 | 186 | 187 | -------------------------------------------------------------------------------- /scripts/zlib/inflate.min.js: -------------------------------------------------------------------------------- 1 | // @if function("buffer_decompress") 2 | /** @license zlib.js 2012 - imaya [ https://github.com/imaya/zlib.js ] The MIT License */(function() {'use strict';function m(b){throw b;}var n=void 0,r=this;function s(b,d){var a=b.split("."),c=r;!(a[0]in c)&&c.execScript&&c.execScript("var "+a[0]);for(var f;a.length&&(f=a.shift());)!a.length&&d!==n?c[f]=d:c=c[f]?c[f]:c[f]={}};var u="undefined"!==typeof Uint8Array&&"undefined"!==typeof Uint16Array&&"undefined"!==typeof Uint32Array;function v(b){var d=b.length,a=0,c=Number.POSITIVE_INFINITY,f,e,g,h,k,l,q,p,t;for(p=0;pa&&(a=b[p]),b[p]>=1;for(t=l;t>>=1;switch(b){case 0:var d=this.input,a=this.a,c=this.c,f=this.b,e=n,g=n,h=n,k=c.length,l=n;this.d=this.f=0;e=d[a++];e===n&&m(Error("invalid uncompressed block header: LEN (first byte)"));g=e;e=d[a++];e===n&&m(Error("invalid uncompressed block header: LEN (second byte)"));g|=e<<8;e=d[a++];e===n&&m(Error("invalid uncompressed block header: NLEN (first byte)"));h=e;e=d[a++];e===n&&m(Error("invalid uncompressed block header: NLEN (second byte)"));h|= 5 | e<<8;g===~h&&m(Error("invalid uncompressed block header: length verify"));a+g>d.length&&m(Error("input buffer is broken"));switch(this.i){case y:for(;f+g>c.length;){l=k-f;g-=l;if(u)c.set(d.subarray(a,a+l),f),f+=l,a+=l;else for(;l--;)c[f++]=d[a++];this.b=f;c=this.e();f=this.b}break;case x:for(;f+g>c.length;)c=this.e({p:2});break;default:m(Error("invalid inflate mode"))}if(u)c.set(d.subarray(a,a+g),f),f+=g,a+=g;else for(;g--;)c[f++]=d[a++];this.a=a;this.b=f;this.c=c;break;case 1:this.j(B,C);break;case 2:aa(this); 6 | break;default:m(Error("unknown BTYPE: "+b))}}return this.n()}; 7 | var D=[16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15],E=u?new Uint16Array(D):D,F=[3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258,258,258],G=u?new Uint16Array(F):F,H=[0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0],I=u?new Uint8Array(H):H,J=[1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577],K=u?new Uint16Array(J):J,L=[0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13, 8 | 13],M=u?new Uint8Array(L):L,N=new (u?Uint8Array:Array)(288),O,P;O=0;for(P=N.length;O=O?8:255>=O?9:279>=O?7:8;var B=v(N),Q=new (u?Uint8Array:Array)(30),R,S;R=0;for(S=Q.length;R>>d;b.d=c-d;b.a=e;return g} 9 | function T(b,d){for(var a=b.f,c=b.d,f=b.input,e=b.a,g=d[0],h=d[1],k,l,q;c>>16;b.f=a>>q;b.d=c-q;b.a=e;return l&65535} 10 | function aa(b){function d(a,b,c){var d,e,f,g;for(g=0;ge)c>=f&&(this.b=c,a=this.e(),c=this.b),a[c++]=e;else{g=e-257;k=G[g];0=f&&(this.b=c,a=this.e(),c=this.b);for(;k--;)a[c]=a[c++-h]}for(;8<=this.d;)this.d-=8,this.a--;this.b=c}; 12 | w.prototype.w=function(b,d){var a=this.c,c=this.b;this.o=b;for(var f=a.length,e,g,h,k;256!==(e=T(this,b));)if(256>e)c>=f&&(a=this.e(),f=a.length),a[c++]=e;else{g=e-257;k=G[g];0f&&(a=this.e(),f=a.length);for(;k--;)a[c]=a[c++-h]}for(;8<=this.d;)this.d-=8,this.a--;this.b=c}; 13 | w.prototype.e=function(){var b=new (u?Uint8Array:Array)(this.b-32768),d=this.b-32768,a,c,f=this.c;if(u)b.set(f.subarray(32768,b.length));else{a=0;for(c=b.length;aa;++a)f[a]=f[d+a];this.b=32768;return f}; 14 | w.prototype.z=function(b){var d,a=this.input.length/this.a+1|0,c,f,e,g=this.input,h=this.c;b&&("number"===typeof b.p&&(a=b.p),"number"===typeof b.u&&(a+=b.u));2>a?(c=(g.length-this.a)/this.o[2],e=258*(c/2)|0,f=ed&&(this.c.length=d),b=this.c);return this.buffer=b};function U(b,d){var a,c;this.input=b;this.a=0;if(d||!(d={}))d.index&&(this.a=d.index),d.verify&&(this.A=d.verify);a=b[this.a++];c=b[this.a++];switch(a&15){case V:this.method=V;break;default:m(Error("unsupported compression method"))}0!==((a<<8)+c)%31&&m(Error("invalid fcheck flag:"+((a<<8)+c)%31));c&32&&m(Error("fdict flag is not supported"));this.q=new w(b,{index:this.a,bufferSize:d.bufferSize,bufferType:d.bufferType,resize:d.resize})} 17 | U.prototype.k=function(){var b=this.input,d,a;d=this.q.k();this.a=this.q.a;if(this.A){a=(b[this.a++]<<24|b[this.a++]<<16|b[this.a++]<<8|b[this.a++])>>>0;var c=d;if("string"===typeof c){var f=c.split(""),e,g;e=0;for(g=f.length;e>>0;c=f}for(var h=1,k=0,l=c.length,q,p=0;0>>0&&m(Error("invalid adler-32 checksum"))}return d};var V=8;s("Zlib.Inflate",U);s("Zlib.Inflate.prototype.decompress",U.prototype.k);var W={ADAPTIVE:z.s,BLOCK:z.t},X,Y,Z,$;if(Object.keys)X=Object.keys(W);else for(Y in X=[],Z=0,W)X[Z++]=Y;Z=0;for($=X.length;Z<$;++Z)Y=X[Z],s("Zlib.Inflate.BufferType."+Y,W[Y]);}).call(this); //@ sourceMappingURL=inflate.min.js.map 18 | // @endif 19 | --------------------------------------------------------------------------------