├── .github └── FUNDING.yml ├── LICENSE.md ├── README.md ├── p5play.d.ts ├── p5play.js ├── p5play.min.js ├── package.json └── planck.min.js /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: @quinton-ashley 4 | patreon: q5play 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: q5play 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry 13 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 14 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # p5play Personal License 2 | 3 | By using, copying, modifying, or distributing the p5play software library ("Content"), you ("Licensee") agree to the following terms, as set by Quinton Ashley ("Licensor"), the sole copyright owner. 4 | 5 | This license is intended to enable free, personal use of the Content. 6 | 7 | **_For commercial use, see the [p5play Professional License](https://p5play.org/pro)._** 8 | 9 | **_For educational use, see the [p5play Educational License](https://p5play.org/teach)._** 10 | 11 | ## 1. License Grant 12 | 13 | The Licensor grants the Licensee a non-exclusive, non-transferable, revocable license to use the Content in accordance with these terms. 14 | 15 | If you have entered into a separate written agreement with the Licensor regarding your use of the Content, the terms of that agreement shall prevail to the extent of any conflict with this License. 16 | 17 | ### 1a. Shared Work 18 | 19 | Users can share work that uses the Content only if the work is open source. The entire codebase of the work must be: 20 | 21 | - freely and publicly accessible on the internet, without restrictions 22 | - in human readable form, not minified or obfuscated, but in the preferred form for making modifications 23 | - licensed as AGPL, GPL, CC BY-SA, CC BY, ISC, MIT, or CC0 (public domain) 24 | 25 | The Licensee is permitted to distribute their programs in a compiled or minified form only if the source code is made available alongside it. 26 | 27 | If a web server/client-side web app ("Website") uses the Content as part of the Website's code or in a work generated by the Website's code, the entire codebase for the Website must be open source. Yet, if the Website merely hosts user generated content that uses p5play, only the user is required to make their own content open source. 28 | 29 | ### 1b. Limited Redistribution 30 | 31 | The Licensee may host verbatim, unmodified copies of the Content with a copy of this license alongside it. The Licensee may not sell or sublicense the Content. 32 | 33 | ### 1c. Derivative Works 34 | 35 | Publishing modified versions of the Content ("Derivative Work") is permitted only for the purpose of contributing directly to the original p5play project (for example, by creating a temporary fork to submit a pull request or patch to the official repository). 36 | 37 | The Licensee may not use the Content to create a competing product or service. 38 | 39 | ### 1d. No Commercial Use 40 | 41 | The Licensee is expressly forbidden from using the Content for commercial purposes, which includes any activity that involves monetary exchange, promotes a business entity (including non-profits), or results in direct or indirect financial gain. 42 | 43 | To use the Content commercially, create an account on p5play.org and agree to the [p5play Professional License](https://p5play.org/pro). 44 | 45 | ### 1e. Educational Use Limitation 46 | 47 | Under fair use, the Licensee may produce educational content, such as YouTube tutorials, that explains and showcases use of the Content in a fixed media format, which does not enable direct interaction with the Content. 48 | 49 | The Content can also be used in interactive art that is educational in nature, but the topics covered must not be related to computer programming or game development. For example, a game that teaches players how to cook is permitted. 50 | 51 | Typical academic use of the Content, such as in class instruction or coursework, is prohibited under this License. 52 | 53 | To use the Content for academic purposes, create an account on p5play.org, pay license fees as determined by the Licensor, and agree to the [p5play Educational License](https://p5play.org/teach). This is required even if the Content is used indirectly through a third-party provider. 54 | 55 | ### 1f. No Military Use 56 | 57 | The Licensee is expressly forbidden from using the Content in projects that are directly endorsed by military organizations or military contractors. 58 | 59 | ### 1g. Attribution Required 60 | 61 | The Licensee must publicly display appropriate credit to the Licensor for the Content, but not in any way that suggests the Licensor endorses the Licensee or the Licensee's use, unless the Licensor has given prior written consent. 62 | 63 | Use of the "made with p5play" Loading Screen is required for all published work that uses p5play, unless the work is published on an online code editing platform or other web app that was approved by the Licensor to be exempt from this requirement, in which case the loading screen will not appear. Attribution can then be made at the top of the primary script file or in a README file. The following is an example of acceptable attribution: 64 | 65 | ```js 66 | /** 67 | * Made with p5play! 68 | * https://p5play.org 69 | */ 70 | ``` 71 | 72 | ## 2. Licensee Eligibility Requirements 73 | 74 | Anyone can use the Content for free under this License, provided they comply with the terms and conditions set forth herein. 75 | 76 | ## 3. Privacy 77 | 78 | Licensees optionally consent to p5play using Google Analytics to track how they use p5play. To revoke this consent, you can use a browser extension to block Google Analytics or set `window._p5play_gtagged = false;` before loading p5play. 79 | 80 | ## 4. No Warranty 81 | 82 | The Content is provided "as is." The Licensor makes no warranties, express or implied, including without limitation any implied warranty of merchantability or fitness for a particular purpose, concerning the Content. 83 | 84 | ## 5. Limitation of Liability 85 | 86 | The Licensor shall not be liable for any damages suffered by the Licensee resulting from the use or inability to use the Content. 87 | 88 | Licensees must comply with all applicable laws and regulations when using p5play. It is illegal to create content that infringes on the rights of others. Licensees are fully liable for any content they create with p5play. 89 | 90 | ## 6. Amendments 91 | 92 | This Agreement can be amended by the Licensor at any time. To the extent possible, if any provision of this Public License is deemed unenforceable, it shall be automatically reformed to the minimum extent necessary to make it enforceable. If the provision cannot be reformed, it shall be severed from this Public License without affecting the enforceability of the remaining terms and conditions. 93 | 94 | ## 7. Termination 95 | 96 | Termination of the Agreement will occur if the Licensee fails to comply with any term(s) of this Agreement. Therefore, termination can occur automatically, without notice from the Licensor. 97 | 98 | Upon termination, the Licensee must cease all use and distribution of the Content. 99 | 100 | ## 8. Entire Agreement 101 | 102 | This Agreement constitutes the entire agreement between the parties and supersedes all prior understandings and agreements, whether written or oral, relating to the subject matter of this Agreement. 103 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # p5play ![](https://img.shields.io/github/package-json/v/quinton-ashley/p5play) 2 | 3 | ## Visit [p5play.org][]! 🎮🧑‍💻 4 | 5 | p5play is a JavaScript game engine that uses q5.js/p5.js for graphics and Box2D for physics. 6 | 7 | Join us on [Discord][] to ask questions and meet other people making games with p5play. 👾 8 | 9 | The [p5play Personal License][] enables free, open source, personal use. 10 | 11 | Educational use requires the [p5play Educational License][]. 12 | 13 | Commercial game development requires the [p5play Professional License][]. 14 | 15 | ## Credits ⭐️ 16 | 17 | p5play (version 3) was created by Quinton Ashley. 18 | 19 | Huge thanks to everyone that's helped with p5play! 🙏 20 | 21 | Paolo Pedercini, creator of p5.play (version 1), who trusted me to take over the project. 22 | 23 | Erin Catto, creator of the Box2D physics simulator. 24 | 25 | Ali Shakiba, creator of planck.js, a JavaScript port of Box2D used behind the scenes by p5play. 26 | 27 | Contributors: @Tezumie, Oliver Zell, Caleb Foss, and Bobby S. 28 | 29 | Testers and bug reporters: Lachlan Noble, Raj Raizada, Nathan Anil, Chayarat Wangweera, Zhiyuan Guo, Aarnav Gupta, and Carter Noa. 30 | 31 | ## Contributing to p5play ✍️ 32 | 33 | Take a look at the [p5play project planning page][]. Let me know what you'd like to help with! See the [p5play-web][] repo for more info. 34 | 35 | [p5play.org]: https://p5play.org 36 | [@quinton-ashley]: https://github.com/quinton-ashley 37 | [@molleindustria]: https://github.com/molleindustria 38 | [Discord]: https://discord.gg/EJwnJATmj7 39 | [p5play Personal License]: https://github.com/quinton-ashley/p5play/blob/main/LICENSE.md 40 | [p5play Educational License]: https://p5play.org/teach 41 | [p5play Professional License]: https://p5play.org/pro 42 | [p5play project planning page]: https://github.com/users/quinton-ashley/projects/5 43 | [p5play-web]: https://github.com/quinton-ashley/p5play-web 44 | -------------------------------------------------------------------------------- /p5play.d.ts: -------------------------------------------------------------------------------- 1 | import 'q5'; 2 | 3 | declare global { 4 | 5 | class P5Play { 6 | /** 7 | * Contains all the sprites in the sketch, 8 | * but users should use the `allSprites` group. 9 | * 10 | * The keys are the sprite's unique ids. 11 | * @type {Object.} 12 | */ 13 | sprites: { 14 | [x: number]: Sprite; 15 | }; 16 | /** 17 | * Contains all the groups in the sketch, 18 | * 19 | * The keys are the group's unique ids. 20 | * @type {Object.} 21 | */ 22 | groups: { 23 | [x: number]: Group; 24 | }; 25 | groupsCreated: number; 26 | spritesCreated: number; 27 | spritesDrawn: number; 28 | /** 29 | * Cache for loaded images. 30 | */ 31 | images: {}; 32 | /** 33 | * Used for debugging, set to true to make p5play 34 | * not load any images. 35 | * @type {Boolean} 36 | * @default false 37 | */ 38 | disableImages: boolean; 39 | /** 40 | * The default color palette, at index 0 of this array, 41 | * has all the letters of the English alphabet mapped to colors. 42 | * @type {Array} 43 | */ 44 | palettes: any[]; 45 | /** 46 | * Emoji scale factor, used when making emoji images. 47 | * @type {Number} 48 | * @default 1 49 | */ 50 | emojiScale: number; 51 | /** 52 | * Friendly rounding eliminates some floating point errors. 53 | * @type {Boolean} 54 | * @default true 55 | */ 56 | friendlyRounding: boolean; 57 | /** 58 | * Groups that are removed using `group.remove()` are not 59 | * fully deleted from `p5play.groups` by default, so their data 60 | * is still accessible. Set to false to permanently delete 61 | * removed groups, which reduces memory usage. 62 | * @type {Boolean} 63 | * @default true 64 | */ 65 | storeRemovedGroupRefs: boolean; 66 | /** 67 | * Snaps sprites to the nearest `p5play.gridSize` 68 | * increment when they are moved. 69 | * @type {Boolean} 70 | * @default false 71 | */ 72 | snapToGrid: boolean; 73 | /** 74 | * The size of the grid cells that sprites are snapped to. 75 | * @type {Number} 76 | * @default 0.5 77 | */ 78 | gridSize: number; 79 | /** 80 | * Information about the operating system being used to run 81 | * p5play, retrieved from the `navigator` object. 82 | */ 83 | os: {}; 84 | context: string; 85 | hasMouse: boolean; 86 | standardizeKeyboard: boolean; 87 | /** 88 | * Displays the number of sprites drawn, the current FPS 89 | * as well as the average, minimum, and maximum FPS achieved 90 | * during the previous second. 91 | * 92 | * FPS in this context refers to how many frames per second your 93 | * computer can generate, based on the physics calculations and any 94 | * other processes necessary to generate a frame, but not 95 | * including the delay between when frames are actually shown on 96 | * the screen. The higher the FPS, the better your game is 97 | * performing. 98 | * 99 | * You can use this function for approximate performance testing. 100 | * But for the most accurate results, use your web browser's 101 | * performance testing tools. 102 | * 103 | * Generally having less sprites and using a smaller canvas will 104 | * make your game perform better. Also drawing images is faster 105 | * than drawing shapes. 106 | * @type {Boolean} 107 | * @default false 108 | */ 109 | renderStats: boolean; 110 | /** 111 | * This function is called when an image is loaded. By default it 112 | * does nothing, but it can be overridden. 113 | */ 114 | onImageLoad(): void; 115 | } 116 | var p5play: P5Play; 117 | var DYN: string; 118 | var DYNAMIC: string; 119 | var STA: string; 120 | var STATIC: string; 121 | var KIN: string; 122 | var KINEMATIC: string; 123 | /** 124 | * @class 125 | */ 126 | class Sprite { 127 | /** 128 | * 129 | * Look at the Sprite reference pages before reading these docs. 130 | * 131 | * 132 | * Creates a new sprite. 133 | * 134 | * All parameters are optional. A sprite's default position is in the 135 | * center of the canvas and default box collider is 50x50 pixels. 136 | * 137 | * Depending on the input parameters, the sprite can be created with 138 | * a box, circle, polygon, or chain collider. 139 | * 140 | * Special feature! If the first parameter to this constructor is a 141 | * loaded Image, Ani, or name of a animation, 142 | * then the Sprite will be created with that animation. If the 143 | * dimensions of the sprite are not given, then the Sprite will be 144 | * created using the dimensions of the animation. 145 | * 146 | * Every sprite you create is added to the `allSprites` 147 | * group and put on the top draw order layer, in front of all 148 | * previously created sprites. 149 | * 150 | * @param {Number} [x] - horizontal position of the sprite 151 | * @param {Number} [y] - vertical position of the sprite 152 | * @param {Number} [w] - width of the placeholder rectangle and of 153 | * the collider until an image or new collider are set. *OR* If height is not 154 | * set then this parameter becomes the diameter of the placeholder circle. 155 | * @param {Number} [h] - height of the placeholder rectangle and of the collider 156 | * until an image or new collider are set 157 | * @param {String} [physicsType] - physics type is DYNAMIC by default, can be 158 | * STATIC, KINEMATIC, or NONE 159 | * @example 160 | * 161 | * let spr = new Sprite(); 162 | * 163 | * let rectangle = new Sprite(x, y, width, height); 164 | * 165 | * let circle = new Sprite(x, y, diameter); 166 | * 167 | * let spr = new Sprite(aniName, x, y); 168 | * 169 | * let line = new Sprite(x, y, [length, angle]); 170 | */ 171 | constructor(x?: number, y?: number, w?: number, h?: number, physicsType?: string, ...args: any[]); 172 | /** 173 | * Each sprite has a unique id number. Don't change it! 174 | * They are useful for debugging. 175 | * @type {Number} 176 | */ 177 | idNum: number; 178 | /** 179 | * Groups the sprite belongs to, including allSprites 180 | * @type {Group[]} 181 | * @default [allSprites] 182 | */ 183 | groups: Group[]; 184 | /** 185 | * Keys are the animation label, values are Ani objects. 186 | * @type {Anis} 187 | */ 188 | animations: Anis; 189 | /** 190 | * Joints that the sprite is attached to 191 | * @type {Joint[]} 192 | * @default [] 193 | */ 194 | joints: Joint[]; 195 | /** 196 | * If set to true, p5play will record all changes to the sprite's 197 | * properties in its `mod` array. Intended to be used to enable 198 | * online multiplayer. 199 | * @type {Boolean} 200 | * @default undefined 201 | */ 202 | watch: boolean; 203 | /** 204 | * An Object that has sprite property number codes as keys, 205 | * these correspond to the index of the property in the 206 | * Sprite.props array. The booleans values this object stores, 207 | * indicate which properties were changed since the last frame. 208 | * Useful for limiting the amount of sprite data sent in binary 209 | * netcode to only the sprite properties that have been modified. 210 | * @type {Object} 211 | */ 212 | mod: any; 213 | set tileSize(val: number); 214 | /** 215 | * DEPRECATED: Will be removed in version 4. 216 | * 217 | * The tile size is used to change the size of one unit of 218 | * measurement for the sprite. 219 | * 220 | * For example, if the tile size is 16, then a sprite with 221 | * x=1 and y=1 will be drawn at position (16, 16) on the canvas. 222 | * @deprecated 223 | * @type {Number} 224 | * @default 1 225 | */ 226 | get tileSize(): number; 227 | set collider(val: string); 228 | /** 229 | * Deprecated alias for `physicsType`/`physics`. 230 | * @deprecated 231 | * @type {String} 232 | * @default DYNAMIC 233 | */ 234 | get collider(): string; 235 | set x(val: number); 236 | /** 237 | * The horizontal position of the sprite. 238 | * @type {Number} 239 | */ 240 | get x(): number; 241 | set y(val: number); 242 | /** 243 | * The vertical position of the sprite. 244 | * @type {Number} 245 | */ 246 | get y(): number; 247 | set image(img: new (width?: number, height?: number) => HTMLImageElement); 248 | /** 249 | * The sprite's image or current frame of animation. 250 | * 251 | * When `sprite.image` is set, two properties are added: 252 | * 253 | * `sprite.image.offset` determines the x and y position the image 254 | * should be drawn at relative to the sprite's center. 255 | * 256 | * `sprite.image.scale` determines the x and y scale of the image. 257 | * @type {Image} 258 | */ 259 | get image(): new (width?: number, height?: number) => HTMLImageElement; 260 | /** 261 | * Used to detect mouse events with the sprite. 262 | * @type {_SpriteMouse} 263 | */ 264 | mouse: _SpriteMouse; 265 | set shape(val: string); 266 | /** 267 | * The kind of shape: 'box', 'circle', 'chain', or 'polygon'. 268 | * 269 | * If a sprite with a circle shape has its shape type changed to 270 | * chain or polygon, the circle will be turned into a dodecagon. 271 | * @type {String} 272 | * @default box 273 | */ 274 | get shape(): string; 275 | set w(val: number); 276 | /** 277 | * The width of the sprite. 278 | * @type {Number} 279 | */ 280 | get w(): number; 281 | set h(val: number); 282 | /** 283 | * The height of the sprite. 284 | * @type {Number} 285 | */ 286 | get h(): number; 287 | /** 288 | * The sprite's position on the previous frame. 289 | * @type {object} 290 | */ 291 | prevPos: object; 292 | prevRotation: number; 293 | /** 294 | * Text displayed at the center of the sprite. 295 | * @type {String} 296 | * @default undefined 297 | */ 298 | text: string; 299 | /** 300 | * Adds a collider (fixture) to the sprite's physics body. 301 | * 302 | * It accepts parameters in a similar format to the Sprite 303 | * constructor except the first two parameters are x and y offsets, 304 | * the distance new collider should be from the center of the sprite. 305 | * 306 | * This function also recalculates the sprite's mass based on the 307 | * size of the new collider added to it. However, it does not move 308 | * the sprite's center of mass, which makes adding multiple colliders 309 | * to a sprite easier. 310 | * 311 | * For better physics simulation results, run the `resetCenterOfMass` 312 | * function after you finish adding colliders to a sprite. 313 | * 314 | * One limitation of the current implementation is that sprites 315 | * with multiple colliders can't have their collider 316 | * type changed without losing every collider added to the 317 | * sprite besides the first. 318 | * 319 | * @param {Number} offsetX - distance from the center of the sprite 320 | * @param {Number} offsetY - distance from the center of the sprite 321 | * @param {Number} w - width of the collider 322 | * @param {Number} h - height of the collider 323 | */ 324 | addCollider(offsetX: number, offsetY: number, w: number, h: number, ...args: any[]): void; 325 | body: any; 326 | /** 327 | * Adds a sensor to the sprite's physics body. 328 | * 329 | * Sensors can't displace or be displaced by colliders. 330 | * Sensors don't have any mass or other physical properties. 331 | * Sensors simply detect overlaps with other sensors. 332 | * 333 | * This function accepts parameters in a similar format to the Sprite 334 | * constructor except the first two parameters are x and y offsets, 335 | * the relative distance the new sensor should be from the center of 336 | * the sprite. 337 | * 338 | * If a sensor is added to a sprite that has no collider (type "none") 339 | * then internally it will be given a dynamic physics body that isn't 340 | * affected by gravity so that the sensor can be added to it. 341 | * 342 | * @param {Number} offsetX - distance from the center of the sprite 343 | * @param {Number} offsetY - distance from the center of the sprite 344 | * @param {Number} w - width of the collider 345 | * @param {Number} h - height of the collider 346 | */ 347 | addSensor(offsetX: number, offsetY: number, w: number, h: number, ...args: any[]): void; 348 | set mass(val: number); 349 | /** 350 | * The mass of the sprite's physics body. 351 | * @type {Number} 352 | */ 353 | get mass(): number; 354 | set rotation(val: number); 355 | /** 356 | * The angle of the sprite's rotation, not the direction it's moving. 357 | * 358 | * If angleMode is set to "degrees", the value will be returned in 359 | * a range of -180 to 180. 360 | * @type {Number} 361 | * @default 0 362 | */ 363 | get rotation(): number; 364 | set vel(val: Vector); 365 | /** 366 | * The sprite's velocity vector {x, y} 367 | * @type {Vector} 368 | * @default {x: 0, y: 0} 369 | */ 370 | get vel(): Vector; 371 | /** 372 | * Removes the physics body colliders from the sprite but not 373 | * overlap sensors. 374 | */ 375 | removeColliders(): void; 376 | /** 377 | * Removes overlap sensors from the sprite. 378 | */ 379 | removeSensors(): void; 380 | set animation(val: Ani); 381 | /** 382 | * Reference to the sprite's current animation. 383 | * @type {Ani} 384 | */ 385 | get animation(): Ani; 386 | set ani(val: Ani); 387 | /** 388 | * Reference to the sprite's current animation. 389 | * @type {Ani} 390 | */ 391 | get ani(): Ani; 392 | /** 393 | * Keys are the animation label, values are Ani objects 394 | * @type {Anis} 395 | */ 396 | get anis(): Anis; 397 | set autoUpdate(val: boolean); 398 | /** 399 | * Controls whether a sprite is updated before each physics update, 400 | * when users let p5play automatically manage the frame cycle. 401 | * @type {Boolean} 402 | * @default true 403 | */ 404 | get autoUpdate(): boolean; 405 | set autoDraw(val: boolean); 406 | /** 407 | * Controls whether a sprite is drawn after each physics update, 408 | * when users let p5play automatically manage the frame cycle. 409 | * @type {Boolean} 410 | * @default true 411 | */ 412 | get autoDraw(): boolean; 413 | set allowSleeping(val: boolean); 414 | /** 415 | * Controls the ability for a sprite to "sleep". 416 | * 417 | * "Sleeping" sprites are not included in the physics simulation, a 418 | * sprite starts "sleeping" when it stops moving and doesn't collide 419 | * with anything that it wasn't already touching. 420 | * @type {Boolean} 421 | * @default true 422 | */ 423 | get allowSleeping(): boolean; 424 | set bounciness(val: number); 425 | /** 426 | * The bounciness of the sprite's physics body. 427 | * @type {Number} 428 | * @default 0.2 429 | */ 430 | get bounciness(): number; 431 | set rotationSpeed(val: number); 432 | /** 433 | * The speed of the sprite's rotation in angles per frame. 434 | * @type {Number} 435 | * @default 0 436 | */ 437 | get rotationSpeed(): number; 438 | set physicsType(val: string); 439 | /** 440 | * Verbose alias for `physics`. 441 | * @type {String} 442 | * @default DYNAMIC 443 | */ 444 | get physicsType(): string; 445 | set physics(val: string); 446 | /** 447 | * The sprite's physics type. Default is DYNAMIC. 448 | * 449 | * The physics type can be one of the following: 450 | * DYNAMIC, STATIC, KINEMATIC, or NONE. 451 | * 452 | * DYN, STA, and KIN can be used as shorthand. 453 | * 454 | * When a sprite's physics type is changed to NONE, 455 | * or from NONE to another type, the sprite will 456 | * maintain its current position, velocity, rotation, and 457 | * rotation speed. 458 | * 459 | * Sprites can't have their physics type 460 | * set to NONE if they have a polygon or chain collider, 461 | * multiple colliders, or multiple sensors. 462 | * 463 | * To achieve the same effect as setting physics type 464 | * to NONE, use `.overlaps(allSprites)` to have your 465 | * sprite overlap with all sprites. 466 | * 467 | * @type {String} 468 | * @default DYNAMIC 469 | */ 470 | get physics(): string; 471 | set color(val: Color); 472 | /** 473 | * The sprite's current color. By default sprites get a random color. 474 | * @type {Color} 475 | * @default random color 476 | */ 477 | get color(): Color; 478 | set colour(val: Color); 479 | /** 480 | * Alias for color. colour is the British English spelling. 481 | * @type {Color} 482 | * @default random color 483 | */ 484 | get colour(): Color; 485 | set fill(val: Color); 486 | /** 487 | * Alias for sprite.fillColor 488 | * @type {Color} 489 | * @default random color 490 | */ 491 | get fill(): Color; 492 | set stroke(val: Color); 493 | /** 494 | * Overrides sprite's stroke color. By default the stroke of a sprite 495 | * is determined by its collider type, which can also be overridden 496 | * by the sketch's stroke color. 497 | * @type {Color} 498 | * @default undefined 499 | */ 500 | get stroke(): Color; 501 | set strokeWeight(val: number); 502 | /** 503 | * The sprite's stroke weight, the thickness of its outline. 504 | * @type {Number} 505 | * @default undefined 506 | */ 507 | get strokeWeight(): number; 508 | set textColor(val: Color); 509 | /** 510 | * The sprite's text fill color. Black by default. 511 | * @type {Color} 512 | * @default black (#000000) 513 | */ 514 | get textColor(): Color; 515 | set textColour(val: any); 516 | get textColour(): any; 517 | set textFill(val: Color); 518 | /** 519 | * The sprite's text fill color. Black by default. 520 | * @type {Color} 521 | * @default black (#000000) 522 | */ 523 | get textFill(): Color; 524 | set textSize(val: number); 525 | /** 526 | * The sprite's text size, the sketch's current textSize by default. 527 | * @type {Number} 528 | */ 529 | get textSize(): number; 530 | set textStroke(val: Color); 531 | /** 532 | * The sprite's text stroke color. 533 | * No stroke by default, does not inherit from the sketch's stroke color. 534 | * @type {Color} 535 | * @default undefined 536 | */ 537 | get textStroke(): Color; 538 | set textStrokeWeight(val: number); 539 | /** 540 | * The sprite's text stroke weight, the thickness of its outline. 541 | * No stroke by default, does not inherit from the sketch's stroke weight. 542 | * @type {Number} 543 | * @default undefined 544 | */ 545 | get textStrokeWeight(): number; 546 | set tile(val: string); 547 | /** 548 | * The tile string represents the sprite in a tile map. 549 | * @type {String} 550 | */ 551 | get tile(): string; 552 | set bearing(val: number); 553 | /** 554 | * A bearing indicates the direction that needs to be followed to 555 | * reach a destination. Setting a sprite's bearing doesn't do 556 | * anything by itself. You can apply a force at the sprite's 557 | * bearing angle using the `applyForce` function. 558 | * @type {Number} 559 | * @example 560 | * sprite.bearing = angle; 561 | * sprite.applyForce(amount); 562 | */ 563 | get bearing(): number; 564 | set debug(val: boolean); 565 | /** 566 | * If true, an outline of the sprite's collider will be drawn. 567 | * @type {Boolean} 568 | * @default false 569 | */ 570 | get debug(): boolean; 571 | set density(val: number); 572 | /** 573 | * The density of the sprite's physics body. 574 | * @type {Number} 575 | * @default 5 576 | */ 577 | get density(): number; 578 | set direction(val: number); 579 | /** 580 | * The angle of the sprite's movement. 581 | * @type {Number} 582 | * @default 0 ("right") 583 | */ 584 | get direction(): number; 585 | set drag(val: number); 586 | /** 587 | * The amount of resistance a sprite has to being moved. 588 | * @type {Number} 589 | * @default 0 590 | */ 591 | get drag(): number; 592 | set draw(val: Function); 593 | /** 594 | * Displays the sprite. 595 | * 596 | * This function is called automatically at the end of each 597 | * sketch `draw` function call but it can also be run 598 | * by users to customize the order sprites are drawn in relation 599 | * to other stuff drawn to the canvas. Also see the sprite.layer 600 | * property. 601 | * 602 | * A sprite's draw function can be overridden with a 603 | * custom draw function, inside this function (0, 0) is the center of 604 | * the sprite. 605 | * 606 | * Using this function actually calls the sprite's internal `_display` 607 | * function, which sets up the canvas for drawing the sprite before 608 | * calling the sprite's `_draw` function. See the example below for how to 609 | * run the sprite's default `_draw` function inside your custom `draw` function. 610 | * 611 | * @type {Function} 612 | * @example 613 | * let defaultDraw = sprite._draw; 614 | * 615 | * sprite.draw = function() { 616 | * // add custom code here 617 | * defaultDraw(); 618 | * } 619 | */ 620 | get draw(): Function; 621 | set dynamic(val: boolean); 622 | /** 623 | * True if the sprite's physics body is dynamic. 624 | * @type {Boolean} 625 | * @default true 626 | */ 627 | get dynamic(): boolean; 628 | /** 629 | * Returns the first node in a linked list of the planck physics 630 | * body's fixtures. 631 | */ 632 | get fixture(): any; 633 | /** 634 | * Returns the first node in a linked list of the planck physics 635 | * body's fixtures. 636 | */ 637 | get fixtureList(): any; 638 | set friction(val: number); 639 | /** 640 | * The amount the sprite's physics body resists moving 641 | * when rubbing against another physics body. 642 | * @type {Number} 643 | * @default 0.5 644 | */ 645 | get friction(): number; 646 | set heading(val: string); 647 | /** 648 | * The sprite's heading. This is a string that can be set to 649 | * "up", "down", "left", "right", "upRight", "upLeft", "downRight" 650 | * 651 | * It ignores cardinal direction word order, capitalization, spaces, 652 | * underscores, and dashes. 653 | * @type {String} 654 | * @default undefined 655 | */ 656 | get heading(): string; 657 | set img(val: new (width?: number, height?: number) => HTMLImageElement); 658 | /** 659 | * Alias for `sprite.image`. 660 | * @type {Image} 661 | */ 662 | get img(): new (width?: number, height?: number) => HTMLImageElement; 663 | /** 664 | * Read only. True if the sprite is moving. 665 | * @type {Boolean} 666 | */ 667 | get isMoving(): boolean; 668 | set isSuperFast(val: boolean); 669 | /** 670 | * Set this to true if the sprite goes really fast to prevent 671 | * inaccurate physics simulation. 672 | * @type {Boolean} 673 | * @default false 674 | */ 675 | get isSuperFast(): boolean; 676 | set kinematic(val: boolean); 677 | /** 678 | * True if the sprite's physics body is kinematic. 679 | * @type {Boolean} 680 | * @default false 681 | */ 682 | get kinematic(): boolean; 683 | set layer(val: number); 684 | /** 685 | * By default sprites are drawn in the order they were created in. 686 | * You can change the draw order by editing sprite's layer 687 | * property. Sprites with the highest layer value get drawn first. 688 | * @type {Number} 689 | */ 690 | get layer(): number; 691 | set life(val: number); 692 | /** 693 | * When the physics simulation is progressed in `world.physicsUpdate`, 694 | * each sprite's life is decreased by `world.timeScale`. 695 | * 696 | * If life becomes less than or equal to 0, the sprite will 697 | * be removed. 698 | * 699 | * It must be set to a positive integer lower than the max value of 700 | * a 32 bit signed integer, 2147483647, which is the default value 701 | * representing infinite life. This limitation makes sprite netcode 702 | * smaller. But don't worry, at 60 fps this gives users a definable 703 | * sprite life range between 1 frame and ~411 days! 704 | * @type {Number} 705 | * @default 2147483647 706 | */ 707 | get life(): number; 708 | /** 709 | * Recalculates the sprite's mass based on its current 710 | * density and size. 711 | * 712 | * Does not change the sprite's center of mass, to do so 713 | * use the `resetCenterOfMass` function. 714 | */ 715 | resetMass(): void; 716 | /** 717 | * Recalculates the sprite's center of mass based on the masses of 718 | * its fixtures and their positions. Moves the sprite's center to 719 | * the new center of mass, but doesn't actually change the positions 720 | * of its fixtures relative to the world. 721 | * 722 | * In p5play a sprite's center (position) is always the same as its 723 | * center of mass and center of rotation. 724 | */ 725 | resetCenterOfMass(): void; 726 | set mirror(val: any); 727 | /** 728 | * DEPRECATED: Will be removed in version 4. 729 | * 730 | * Use sprite.scale instead. 731 | * @deprecated 732 | * @type {Object} 733 | * @property {Boolean} x - the sprite's horizontal mirror state 734 | * @property {Boolean} y - the sprite's vertical mirror state 735 | * @default {x: false, y: false} 736 | */ 737 | get mirror(): any; 738 | set offset(val: object); 739 | /** 740 | * Offsetting the sprite moves the sprite's physics body relative 741 | * to its center. 742 | * 743 | * The sprite's x and y properties represent its center in world 744 | * coordinates. This point is also the sprite's center of rotation. 745 | * @type {object} 746 | * @property {Number} x - the sprite's horizontal offset 747 | * @property {Number} y - the sprite's vertical offset 748 | * @default {x: 0, y: 0} 749 | */ 750 | get offset(): object; 751 | set opacity(val: number); 752 | /** 753 | * The sprite's opacity. 0 is transparent, 1 is opaque. 754 | * @type {Number} 755 | * @default 1 756 | */ 757 | get opacity(): number; 758 | set previousPosition(val: any); 759 | /** 760 | * Alias for sprite.prevPos 761 | * @type {Object} 762 | */ 763 | get previousPosition(): any; 764 | set previousRotation(val: number); 765 | /** 766 | * Alias for sprite.prevRotation 767 | * @type {Number} 768 | */ 769 | get previousRotation(): number; 770 | set pixelPerfect(val: boolean); 771 | /** 772 | * By default p5play draws sprites with subpixel rendering. 773 | * 774 | * Set pixelPerfect to true to make p5play always display sprites 775 | * at integer pixel precision. This is useful for making retro games. 776 | * @type {Boolean} 777 | * @default false 778 | */ 779 | get pixelPerfect(): boolean; 780 | set removed(val: boolean); 781 | /** 782 | * If the sprite has been removed from the world. 783 | * @type {Boolean} 784 | * @default false 785 | */ 786 | get removed(): boolean; 787 | set rotationDrag(val: number); 788 | /** 789 | * The amount the sprite resists rotating. 790 | * @type {Number} 791 | * @default 0 792 | */ 793 | get rotationDrag(): number; 794 | set rotationLock(val: boolean); 795 | /** 796 | * If true, the sprite can not rotate. 797 | * @type {Boolean} 798 | * @default false 799 | */ 800 | get rotationLock(): boolean; 801 | set scale(val: number | any); 802 | /** 803 | * Scale of the sprite's physics body. Default is {x: 1, y: 1} 804 | * 805 | * The getter for sprite.scale returns the scale as an object with 806 | * x and y properties. 807 | * 808 | * The valueOf function for sprite.scale returns the scale as a 809 | * number. This enables users to do things like `sprite.scale *= 2` 810 | * to double the sprite's scale. 811 | * @type {Number|Object} 812 | * @default 1 813 | */ 814 | get scale(): number | any; 815 | set sleeping(val: boolean); 816 | /** 817 | * Wake a sprite up or put it to sleep. 818 | * 819 | * "Sleeping" sprites are not included in the physics simulation, a 820 | * sprite starts "sleeping" when it stops moving and doesn't collide 821 | * with anything that it wasn't already touching. 822 | * @type {Boolean} 823 | * @default true 824 | */ 825 | get sleeping(): boolean; 826 | set speed(val: number); 827 | /** 828 | * The sprite's speed. 829 | * 830 | * Setting speed to a negative value will make the sprite move 831 | * 180 degrees opposite of its current direction angle. 832 | * @type {Number} 833 | * @default 0 834 | */ 835 | get speed(): number; 836 | set static(val: boolean); 837 | /** 838 | * Is the sprite's physics collider static? 839 | * @type {Boolean} 840 | * @default false 841 | */ 842 | get static(): boolean; 843 | set tint(val: Color); 844 | /** 845 | * Tint color applied to the sprite when drawn. 846 | * 847 | * Note that this is not good for performance, you should probably 848 | * pre-render the effect if you want to use it a lot. 849 | * @type {Color} 850 | * @default undefined 851 | */ 852 | get tint(): Color; 853 | set tintColor(val: Color); 854 | /** 855 | * Alias for sprite.tint 856 | * @type {Color} 857 | * @default undefined 858 | */ 859 | get tintColor(): Color; 860 | /** 861 | * The sprite's vertices, in vertex mode format. 862 | * @type {Array} 863 | */ 864 | set vertices(val: any[]); 865 | get vertices(): any[]; 866 | set visible(val: boolean); 867 | /** 868 | * If true the sprite is shown, if set to false the sprite is hidden. 869 | * 870 | * Becomes null when the sprite is off screen but will be drawn and 871 | * set to true again if it goes back on screen. 872 | * @type {Boolean} 873 | * @default true 874 | */ 875 | get visible(): boolean; 876 | set pos(val: Vector); 877 | /** 878 | * The position vector {x, y} 879 | * @type {Vector} 880 | */ 881 | get pos(): Vector; 882 | set position(val: Vector); 883 | /** 884 | * The position vector {x, y} 885 | * @type {Vector} 886 | */ 887 | get position(): Vector; 888 | /** 889 | * The sprite's absolute position on the canvas. Read only. 890 | */ 891 | get canvasPos(): any; 892 | set hw(val: number); 893 | /** 894 | * Half the width of the sprite. 895 | * @type {Number} 896 | */ 897 | get hw(): number; 898 | set width(val: number); 899 | /** 900 | * The width of the sprite. 901 | * @type {Number} 902 | */ 903 | get width(): number; 904 | set halfWidth(val: number); 905 | /** 906 | * Half the width of the sprite. 907 | * @type {Number} 908 | */ 909 | get halfWidth(): number; 910 | set hh(val: number); 911 | /** 912 | * Half the height of the sprite. 913 | * @type {Number} 914 | */ 915 | get hh(): number; 916 | set height(val: number); 917 | /** 918 | * The height of the sprite. 919 | * @type {Number} 920 | */ 921 | get height(): number; 922 | set halfHeight(val: number); 923 | /** 924 | * Half the height of the sprite. 925 | * @type {Number} 926 | */ 927 | get halfHeight(): number; 928 | set d(val: number); 929 | /** 930 | * The diameter of a circular sprite. 931 | * @type {Number} 932 | */ 933 | get d(): number; 934 | set diameter(val: number); 935 | /** 936 | * The diameter of a circular sprite. 937 | * @type {Number} 938 | */ 939 | get diameter(): number; 940 | set r(val: number); 941 | /** 942 | * The radius of a circular sprite. 943 | * @type {Number} 944 | */ 945 | get r(): number; 946 | set radius(val: number); 947 | /** 948 | * The radius of a circular sprite. 949 | * @type {Number} 950 | */ 951 | get radius(): number; 952 | set update(val: Function); 953 | /** 954 | * Runs before each physics update by default, when p5play is automatically 955 | * managing the frame cycle. 956 | * 957 | * Set this to a custom function that handles input, directs sprite movement, 958 | * and performs other tasks that should run before the physics update. 959 | * 960 | * Optionally, users can run this function manually in p5play's `update` 961 | * function. 962 | * @type {Function} 963 | */ 964 | get update(): Function; 965 | set postDraw(val: Function); 966 | /** 967 | * Runs at the end of the p5play frame cycle. 968 | * 969 | * Users should not directly run this function. 970 | * @type {Function} 971 | */ 972 | get postDraw(): Function; 973 | set velocity(val: Vector); 974 | /** 975 | * The sprite's velocity vector {x, y} 976 | * @type {Vector} 977 | * @default {x: 0, y: 0} 978 | */ 979 | get velocity(): Vector; 980 | set gravityScale(val: number); 981 | /** 982 | * A ratio that defines how much the sprite is affected by gravity. 983 | * @type {Number} 984 | * @default 1 985 | */ 986 | get gravityScale(): number; 987 | /** 988 | * If this function is given a force amount, the force is applied 989 | * at the angle of the sprite's current bearing. Force can 990 | * also be given as a vector. 991 | * 992 | * The origin of the force can be given as a vector or as x and y 993 | * coordinates. If no origin is given, the force is applied to the 994 | * center of the sprite. 995 | * 996 | * @param {Number} amount 997 | * @param {Vector} [origin] 998 | * @example 999 | * sprite.applyForce(amount); 1000 | * sprite.applyForce(amount, {x: originX, y: originY}); 1001 | * sprite.applyForce(x, y); 1002 | * sprite.applyForce(x, y, {x: originX, y: originY}); 1003 | * sprite.applyForce({x, y}, {x: originX, y: originY}); 1004 | */ 1005 | applyForce(amount: number, origin?: Vector, ...args: any[]): void; 1006 | /** 1007 | * Applies a force that's scaled to the sprite's mass. 1008 | * 1009 | * @param {Number} amount 1010 | * @param {Vector} [origin] 1011 | */ 1012 | applyForceScaled(amount: number, origin?: Vector, ...args: any[]): void; 1013 | /** 1014 | * Applies a force to the sprite's center of mass attracting it to 1015 | * the given position. 1016 | * 1017 | * Radius and easing not implemented yet! 1018 | * 1019 | * @param {Number} x 1020 | * @param {Number} y 1021 | * @param {Number} force 1022 | * @param {Number} [radius] - infinite if not given 1023 | * @param {Number} [easing] - solid if not given 1024 | * @example 1025 | * sprite.attractTo(x, y, force); 1026 | * sprite.attractTo({x, y}, force); 1027 | */ 1028 | attractTo(x: number, y: number, force: number, radius?: number, easing?: number): void; 1029 | repelFrom(x: any, y: any, force: any, radius: any, easing: any): void; 1030 | /** 1031 | * Apply a torque on the sprite's physics body. 1032 | * Torque is the force that causes rotation. 1033 | * A positive torque will rotate the sprite clockwise. 1034 | * A negative torque will rotate the sprite counter-clockwise. 1035 | * 1036 | * This function is the rotational equivalent of applyForce(). 1037 | * It will not imperatively set the sprite's rotation. 1038 | * 1039 | * @param {Number} torque - The amount of torque to apply. 1040 | */ 1041 | applyTorque(val: any): void; 1042 | /** 1043 | * Moves a sprite towards a position at a percentage of the distance 1044 | * between itself and the destination. 1045 | * 1046 | * @param {Number|Object} x - destination x or an object with x and y properties 1047 | * @param {Number} y - destination y 1048 | * @param {Number} [tracking] - percent of the distance to move towards the destination as a 0-1 value, default is 0.1 (10% tracking) 1049 | */ 1050 | moveTowards(x: number | any, y: number, tracking?: number): void; 1051 | /** 1052 | * Moves the sprite away from a position, the opposite of moveTowards, 1053 | * at a percentage of the distance between itself and the position. 1054 | * @param {Number|Object} x - destination x or an object with x and y properties 1055 | * @param {Number} y - destination y 1056 | * @param {Number} [repel] - percent of the distance to the repel position as a 0-1 value, default is 0.1 (10% repel) 1057 | */ 1058 | moveAway(x: number | any, y: number, repel?: number, ...args: any[]): void; 1059 | /** 1060 | * Move the sprite a distance from its current position. 1061 | * 1062 | * You can specify the `direction` and `speed` of movement as 1063 | * parameters or set these properties before using this function. 1064 | * 1065 | * When `tileSize` is not 1, distance is divisible by 0.5, 1066 | * and a direction name or cardinal direction angle is given, 1067 | * the distance the sprite moves will be rounded up to the 1068 | * nearest half tile. 1069 | * 1070 | * @param {Number} distance 1071 | * @param {Number|String} [direction] - direction angle in degrees or a cardinal direction name, if not given the sprite's current direction is used 1072 | * @param {Number} [speed] - if not given, the sprite's current `speed` is used, unless it's 0 then it's given a default speed of 1 or 0.1 if the sprite's tileSize is greater than 1 1073 | * @returns {Promise} resolves when the movement is complete or cancelled 1074 | * 1075 | * @example 1076 | * sprite.direction = -90; 1077 | * sprite.speed = 2; 1078 | * sprite.move(1); 1079 | * // or 1080 | * sprite.move(1, -90, 2); 1081 | * // or 1082 | * sprite.move('up', 2); 1083 | */ 1084 | move(distance: number, direction?: number | string, speed?: number, ...args: any[]): Promise; 1085 | /** 1086 | * Move the sprite to a position. 1087 | * 1088 | * @param {Number|Object} x - destination x or an object with x and y properties 1089 | * @param {Number} y - destination y 1090 | * @param {Number} [speed] - if not given, the sprite's current speed is used, unless it is 0 then it is given a default speed of 1 or 0.1 if the sprite's tileSize is greater than 1 1091 | * @returns {Promise} resolves to true when the movement is complete 1092 | * or to false if the sprite will not reach its destination 1093 | */ 1094 | moveTo(x: number | any, y: number, speed?: number): Promise; 1095 | /** 1096 | * Rotates the sprite towards an angle or position 1097 | * with x and y properties. 1098 | * 1099 | * @param {Number|Object} angle - angle in degrees or an object with x and y properties 1100 | * @param {Number} [tracking] - percent of the distance to rotate on each frame towards the target angle, default is 0.1 (10%) 1101 | * @param {Number} [facing] - (only specify if position is given) rotation angle the sprite should be at when "facing" the position, default is 0 1102 | */ 1103 | rotateTowards(angle: number | any, tracking?: number, ...args: any[]): void; 1104 | /** 1105 | * Finds the angle from this sprite to the given position or object 1106 | * with x and y properties. 1107 | * 1108 | * Can be used to change the direction of a sprite so it moves 1109 | * to a position or object, as shown in the example. 1110 | * 1111 | * Used internally by `moveTo` and `moveTowards`. 1112 | * 1113 | * @param {Number} x 1114 | * @param {Number} y 1115 | * @returns {Number} angle 1116 | * @example 1117 | * spriteA.direction = spriteA.angleTo(spriteB); 1118 | */ 1119 | angleTo(x: number, y: number): number; 1120 | /** 1121 | * Finds the rotation angle the sprite should be at when "facing" 1122 | * a position. 1123 | * 1124 | * Used internally by `rotateTo`. 1125 | * 1126 | * @param {Number} x 1127 | * @param {Number} y 1128 | * @param {Number} [facing] - relative angle the sprite should be at when "facing" the position, default is 0 1129 | * @returns {Number} the rotation angle the sprite should be at when "facing" the position 1130 | */ 1131 | rotationToFace(x: number, y: number, facing?: number): number; 1132 | /** 1133 | * Finds the minimum angle distance that the sprite would have 1134 | * to rotate to "face" a position at a specified facing rotation, 1135 | * taking into account the current rotation of the sprite. 1136 | * 1137 | * Used internally by `rotateMinTo` and `rotateTowards`. 1138 | * 1139 | * @param {Number} x 1140 | * @param {Number} y 1141 | * @param {Number} facing - relative angle the sprite should be at when "facing" the position, default is 0 1142 | * @returns {Number} the minimum angle distance to face the position 1143 | */ 1144 | angleToFace(x: number, y: number, facing: number): number; 1145 | /** 1146 | * Rotates the sprite to an angle or to face a position 1147 | * at a given rotation speed. 1148 | * 1149 | * @param {Number|Object} angle - angle or a position object with x and y properties 1150 | * @param {Number} [speed] - amount of rotation per frame, if not given the sprite's current `rotationSpeed` is used, if 0 it defaults to 1 1151 | * @param {Number} [facing] - relative angle the sprite should be at when "facing" the given position, default is 0 1152 | * @returns {Promise} a promise that resolves when the rotation is complete 1153 | * @example 1154 | * sprite.rotationSpeed = 2; 1155 | * sprite.rotateTo(180); 1156 | * // or 1157 | * sprite.rotateTo(180, 2); 1158 | * // or 1159 | * // (x, y, speed) 1160 | * sprite.rotateTo(0, 100, 2); 1161 | * // or 1162 | * sprite.rotateTo({x: 0, y: 100}, 2); 1163 | */ 1164 | rotateTo(angle: number | any, speed?: number, facing?: number, ...args: any[]): Promise; 1165 | /** 1166 | * Rotates the sprite by the smallest angular distance 1167 | * to an angle or to face a position at a given absolute 1168 | * rotation speed. 1169 | * 1170 | * @param {Number|Object} angle - angle or a position object with x and y properties 1171 | * @param {Number} speed - absolute amount of rotation per frame, if not given the sprite's current `rotationSpeed` is used 1172 | * @param {Number} facing - relative angle the sprite should be at when "facing" the given position, default is 0 1173 | */ 1174 | rotateMinTo(angle: number | any, speed: number, facing: number, ...args: any[]): Promise; 1175 | /** 1176 | * Rotates the sprite by an angle amount at a given rotation speed. 1177 | * 1178 | * To achieve a clockwise rotation, use a positive angle and speed. 1179 | * To achieve a counter-clockwise rotation, use a negative angle 1180 | * or speed. 1181 | * 1182 | * When the rotation is complete, the sprite's rotation speed is 1183 | * set to 0 and sprite's rotation is set to the exact destination angle. 1184 | * 1185 | * If the angle amount is not evenly divisible by the rotation speed, 1186 | * the sprite's rotation speed will be decreased as it approaches the 1187 | * destination angle. 1188 | * @param {Number} angle - the amount to rotate the sprite 1189 | * @param {Number} [speed] - the absolute amount of rotation per frame, if not given the sprite's current `rotationSpeed` is used, if 0 it defaults to 1 1190 | * @returns {Promise} a promise that resolves when the rotation is complete 1191 | */ 1192 | rotate(angle: number, speed?: number): Promise; 1193 | /** 1194 | * Adds an animation to the sprite. Use this function in the `preload` 1195 | * function. You don't need to name the animation if the sprite will only 1196 | * use one animation. See Ani for more information. 1197 | * 1198 | * If an animation was already added to a different sprite or group, 1199 | * it can not be added to another sprite or group. A clone (copy) of 1200 | * the animation will be automatically created and added instead. 1201 | * 1202 | * @param {String} name - Ani identifier 1203 | * @param {Ani} animation - The preloaded animation 1204 | * @example 1205 | * sprite.addAni(name, animation); 1206 | * sprite.addAni(name, frame1, frame2, frame3...); 1207 | * sprite.addAni(name, atlas); 1208 | */ 1209 | addAni(...args: any[]): any; 1210 | /** 1211 | * Add multiple animations to the sprite. 1212 | * @param {Object} atlases - an object with animation names as keys and 1213 | * an animation or animation atlas as values 1214 | * @example 1215 | * sprite.addAnis({ 1216 | * name0: atlas0, 1217 | * name1: atlas1 1218 | * }); 1219 | */ 1220 | addAnis(...args: any[]): void; 1221 | spriteSheet: any; 1222 | /** 1223 | * Changes the sprite's animation. Use `addAni` to define the 1224 | * animation(s) first. 1225 | * 1226 | * @param {...String} anis - the names of one or many animations to be played in 1227 | * sequence 1228 | * @returns A promise that fulfills when the animation or sequence of animations 1229 | * completes 1230 | */ 1231 | changeAni(...args: string[]): Promise; 1232 | /** 1233 | * Changes the sprite's animation. Use `addAni` to define the 1234 | * animation(s) first. Alt for `changeAni`. 1235 | * 1236 | * @param {...String} anis - the names of one or many animations to be played in 1237 | * sequence 1238 | * @returns A promise that fulfills when the animation or sequence of animations 1239 | * completes 1240 | */ 1241 | changeAnimation(...args: string[]): Promise; 1242 | /** 1243 | * Removes the Sprite from the sketch and all the groups it 1244 | * belongs to. 1245 | * 1246 | * When a sprite is removed it will not be drawn or updated anymore. 1247 | * If it has a physics body, it will be removed from the 1248 | * physics world simulation. 1249 | * 1250 | * There's no way to undo this operation. If you want to hide a 1251 | * sprite use `sprite.visible = false` instead. 1252 | * 1253 | */ 1254 | remove(): void; 1255 | /** 1256 | * Warning: This function might be changed in a future release. 1257 | * 1258 | * Returns the sprite's unique identifier `sprite.idNum`. 1259 | * 1260 | * @returns the sprite's id 1261 | */ 1262 | toString(): string; 1263 | collide(target: any, callback: any): boolean; 1264 | /** 1265 | * Returns true on the first frame that the sprite collides with the 1266 | * target sprite or group. 1267 | * 1268 | * Custom collision event handling can be done by using this function 1269 | * in an if statement or adding a callback as the second parameter. 1270 | * 1271 | * @param {Sprite|Group} target 1272 | * @param {Function} [callback] 1273 | */ 1274 | collides(target: Sprite | Group, callback?: Function): boolean; 1275 | /** 1276 | * Returns a truthy value while the sprite is colliding with the 1277 | * target sprite or group. The value is the number of frames that 1278 | * the sprite has been colliding with the target. 1279 | * 1280 | * @param {Sprite|Group} target 1281 | * @param {Function} [callback] 1282 | * @return {Number} frames 1283 | */ 1284 | colliding(target: Sprite | Group, callback?: Function): number; 1285 | /** 1286 | * Returns true on the first frame that the sprite no longer overlaps 1287 | * with the target sprite or group. 1288 | * 1289 | * @param {Sprite|Group} target 1290 | * @param {Function} [callback] 1291 | * @return {Boolean} 1292 | */ 1293 | collided(target: Sprite | Group, callback?: Function): boolean; 1294 | overlap(target: any, callback: any): boolean; 1295 | /** 1296 | * Returns true on the first frame that the sprite overlaps with the 1297 | * target sprite or group. 1298 | * 1299 | * Custom overlap event handling can be done by using this function 1300 | * in an if statement or adding a callback as the second parameter. 1301 | * 1302 | * @param {Sprite|Group} target 1303 | * @param {Function} [callback] 1304 | */ 1305 | overlaps(target: Sprite | Group, callback?: Function): boolean; 1306 | /** 1307 | * Returns a truthy value while the sprite is overlapping with the 1308 | * target sprite or group. The value returned is the number of 1309 | * frames the sprite has been overlapping with the target. 1310 | * 1311 | * @param {Sprite|Group} target 1312 | * @param {Function} [callback] 1313 | * @return {Number} frames 1314 | */ 1315 | overlapping(target: Sprite | Group, callback?: Function): number; 1316 | /** 1317 | * Returns true on the first frame that the sprite no longer overlaps 1318 | * with the target sprite or group. 1319 | * 1320 | * @param {Sprite|Group} target 1321 | * @param {Function} [callback] 1322 | * @return {Boolean} 1323 | */ 1324 | overlapped(target: Sprite | Group, callback?: Function): boolean; 1325 | /** 1326 | * This function is used internally if a sprite overlap detection 1327 | * function is called but the sprite has no overlap sensors. 1328 | * 1329 | * It creates sensor fixtures that are the same size as the sprite's 1330 | * colliders. If you'd like to add more sensors to a sprite, use the 1331 | * addSensor function. 1332 | */ 1333 | addDefaultSensors(): void; 1334 | /** 1335 | * Returns the distance to another sprite, the mouse, a touch, 1336 | * or any other object with x and y properties. Uses p5's `dist` 1337 | * function. 1338 | * @param {Sprite} o object with x and y properties 1339 | * @returns {Number} distance 1340 | */ 1341 | distanceTo(o: Sprite): number; 1342 | } 1343 | /** 1344 | * @class 1345 | * @extends Array 1346 | */ 1347 | class Ani extends Array HTMLImageElement> { 1348 | /** 1349 | * 1350 | * Look at the Animation reference pages before reading these docs. 1351 | * 1352 | * 1353 | * An `Ani` object contains an array of images (Q5.Image objects) 1354 | * that can be displayed with the `animation` function or by 1355 | * being a sprite's animation. 1356 | * 1357 | * An animation can be created multiple ways, including from: 1358 | * - a list of image file paths as multiple input parameters 1359 | * - a sequence of numbered images by providing the file path to 1360 | * the first image frame and last frame index 1361 | * - a sprite sheet image path and atlas object, frame locator, or 1362 | * frame locators array (see the Learn page on Ani for more info) 1363 | * 1364 | * `Ani` is not a shorthand for `Animation`, since that class name 1365 | * is already used by the JS Web Animations API. 1366 | * 1367 | * @param {...Image} ...images - Image objects to be used as frames 1368 | * @example 1369 | * let shapeShifter = new Ani("dog.png", "cat.png", "snake.png"); 1370 | */ 1371 | constructor(...args: (new (width?: number, height?: number) => HTMLImageElement)[]); 1372 | /** 1373 | * The name of the animation 1374 | * @type {String} 1375 | */ 1376 | name: string; 1377 | targetFrame: number; 1378 | /** 1379 | * The offset is how far the animation should be placed from 1380 | * the location it is played at. 1381 | * @type {Object} 1382 | * @example 1383 | * ani.offset.x = 16; 1384 | */ 1385 | offset: any; 1386 | demoMode: any; 1387 | /** 1388 | * True if the animation is currently playing. 1389 | * @type {Boolean} 1390 | * @default true 1391 | */ 1392 | playing: boolean; 1393 | /** 1394 | * Animation visibility. 1395 | * @type {Boolean} 1396 | * @default true 1397 | */ 1398 | visible: boolean; 1399 | /** 1400 | * If set to false the animation will stop after reaching the last frame 1401 | * @type {Boolean} 1402 | * @default true 1403 | */ 1404 | looping: boolean; 1405 | /** 1406 | * Ends the loop on frame 0 instead of the last frame. 1407 | * This is useful for animations that are symmetric. 1408 | * For example a walking cycle where the first frame is the 1409 | * same as the last frame. 1410 | * @type {Boolean} 1411 | * @default false 1412 | */ 1413 | endOnFirstFrame: boolean; 1414 | /** 1415 | * True if frame changed during the last draw cycle 1416 | * @type {Boolean} 1417 | */ 1418 | frameChanged: boolean; 1419 | onComplete: any; 1420 | onChange: any; 1421 | rotation: any; 1422 | spriteSheet: any; 1423 | set frame(val: number); 1424 | /** 1425 | * The index of the current frame that the animation is on. 1426 | * @type {Number} 1427 | */ 1428 | get frame(): number; 1429 | set frameDelay(val: number); 1430 | /** 1431 | * Delay between frames in number of draw cycles. 1432 | * If set to 4 the framerate of the animation would be the 1433 | * sketch framerate divided by 4 (60fps = 15fps) 1434 | * @type {Number} 1435 | * @default 4 1436 | */ 1437 | get frameDelay(): number; 1438 | set scale(val: number | any); 1439 | /** 1440 | * The animation's scale. 1441 | * 1442 | * Can be set to a number to scale both x and y 1443 | * or an object with x and/or y properties. 1444 | * @type {Number|Object} 1445 | * @default 1 1446 | */ 1447 | get scale(): number | any; 1448 | /** 1449 | * Make a copy of the animation. 1450 | * 1451 | * @return {Ani} A copy of the animation. 1452 | */ 1453 | clone(): Ani; 1454 | /** 1455 | * Draws the animation at coordinate x and y. 1456 | * Updates the frames automatically. 1457 | * 1458 | * Optional parameters effect the current draw cycle only and 1459 | * are not saved between draw cycles. 1460 | * 1461 | * @param {Number} x - horizontal position 1462 | * @param {Number} y - vertical position 1463 | * @param {Number} [r] - rotation 1464 | * @param {Number} [sx] - scale x 1465 | * @param {Number} [sy] - scale y 1466 | */ 1467 | draw(x: number, y: number, r?: number, sx?: number, sy?: number): void; 1468 | x: number; 1469 | y: number; 1470 | update(): void; 1471 | /** 1472 | * Plays the animation, starting from the specified frame. 1473 | * 1474 | * @returns [Promise] a promise that resolves when the animation completes 1475 | */ 1476 | play(frame: any): Promise; 1477 | /** 1478 | * Pauses the animation. 1479 | */ 1480 | pause(frame: any): void; 1481 | /** 1482 | * Stops the animation. Alt for pause. 1483 | */ 1484 | stop(frame: any): void; 1485 | /** 1486 | * Plays the animation backwards. 1487 | * Equivalent to ani.goToFrame(0) 1488 | * 1489 | * @returns [Promise] a promise that resolves when the animation completes 1490 | * rewinding 1491 | */ 1492 | rewind(): Promise; 1493 | /** 1494 | * Plays the animation forwards and loops it. 1495 | */ 1496 | loop(): void; 1497 | /** 1498 | * Prevents the animation from looping 1499 | */ 1500 | noLoop(): void; 1501 | /** 1502 | * Goes to the next frame and stops. 1503 | */ 1504 | nextFrame(): void; 1505 | /** 1506 | * Goes to the previous frame and stops. 1507 | */ 1508 | previousFrame(): void; 1509 | /** 1510 | * Plays the animation forward or backward toward a target frame. 1511 | * 1512 | * @param {Number} toFrame - Frame number destination (starts from 0) 1513 | * @returns [Promise] a promise that resolves when the animation completes 1514 | */ 1515 | goToFrame(toFrame: number): Promise; 1516 | /** 1517 | * The index of the last frame. Read only. 1518 | * @type {Number} 1519 | */ 1520 | get lastFrame(): number; 1521 | /** 1522 | * The current frame as an Image object. Read only. 1523 | * @type {Image} 1524 | */ 1525 | get frameImage(): new (width?: number, height?: number) => HTMLImageElement; 1526 | /** 1527 | * Width of the animation's current frame. 1528 | * @type {Number} 1529 | */ 1530 | get w(): number; 1531 | /** 1532 | * Width of the animation's current frame. 1533 | * @type {Number} 1534 | */ 1535 | get width(): number; 1536 | get defaultWidth(): any; 1537 | /** 1538 | * Height of the animation's current frame. 1539 | * @type {Number} 1540 | */ 1541 | get h(): number; 1542 | /** 1543 | * Height of the animation's current frame. 1544 | * @type {Number} 1545 | */ 1546 | get height(): number; 1547 | get defaultHeight(): any; 1548 | } 1549 | /** 1550 | * 1551 | * Look at the Animation reference pages before reading these docs. 1552 | * 1553 | * 1554 | * This Anis class serves the same role that Group does for Sprites. 1555 | * This class is used internally to create `sprite.anis` 1556 | * and `group.anis`. 1557 | * 1558 | * In instances of this class, the keys are animation names, 1559 | * values are Ani objects. 1560 | * 1561 | * Because users only expect instances of this class to contain 1562 | * animation names as keys, it uses an internal private object 1563 | * `#_` to store animation properties. Getters and setters are used to 1564 | * access the private properties, enabling dynamic inheritance. 1565 | * 1566 | * @class 1567 | */ 1568 | class Anis { 1569 | set width(val: any); 1570 | get width(): any; 1571 | w: any; 1572 | set height(val: any); 1573 | get height(): any; 1574 | h: any; 1575 | #private; 1576 | } 1577 | /** 1578 | * @class 1579 | * @extends Array 1580 | */ 1581 | class Group extends Array { 1582 | /** 1583 | * 1584 | * Look at the Group reference pages before reading these docs. 1585 | * 1586 | * 1587 | * A Group is an array of sprites with similar traits and behaviors. 1588 | * 1589 | * Group extends Array, so you can use them in for of loops. They 1590 | * inherit all the functions and properties of standard arrays 1591 | * such as `group.length` and functions like `group.includes()`. 1592 | * 1593 | * Changing a group setting changes it for all the sprites in the 1594 | * group, similar to class inheritance. Groups can have subgroups, 1595 | * creating a hierarchy of inheritance. 1596 | * 1597 | * The top level group is a q5 instance level variable named 1598 | * `allSprites` that contains all the sprites added to the sketch. 1599 | */ 1600 | constructor(...args: any[]); 1601 | /** 1602 | * @type {Number} 1603 | */ 1604 | x: number; 1605 | /** 1606 | * @type {Number} 1607 | */ 1608 | y: number; 1609 | /** 1610 | * @type {Number} 1611 | */ 1612 | vel: number; 1613 | /** 1614 | * @type {Number} 1615 | */ 1616 | rotation: number; 1617 | /** 1618 | * @type {Number} 1619 | */ 1620 | rotationSpeed: number; 1621 | /** 1622 | * @type {Boolean} 1623 | */ 1624 | autoDraw: boolean; 1625 | /** 1626 | * @type {Boolean} 1627 | */ 1628 | allowSleeping: boolean; 1629 | /** 1630 | * @type {Number} 1631 | */ 1632 | autoUpdate: number; 1633 | /** 1634 | * @type {Number} 1635 | */ 1636 | bounciness: number; 1637 | /** 1638 | * @type {Number} 1639 | */ 1640 | collider: number; 1641 | /** 1642 | * @type {Number} 1643 | */ 1644 | color: number; 1645 | /** 1646 | * @type {Boolean} 1647 | */ 1648 | debug: boolean; 1649 | /** 1650 | * @type {Number} 1651 | */ 1652 | density: number; 1653 | /** 1654 | * @type {Number} 1655 | */ 1656 | direction: number; 1657 | /** 1658 | * @type {Number} 1659 | */ 1660 | drag: number; 1661 | /** 1662 | * @type {Number} 1663 | */ 1664 | friction: number; 1665 | /** 1666 | * @type {Number} 1667 | */ 1668 | h: number; 1669 | /** 1670 | * @type {Boolean} 1671 | */ 1672 | isSuperFast: boolean; 1673 | /** 1674 | * @type {Number} 1675 | */ 1676 | layer: number; 1677 | /** 1678 | * @type {Number} 1679 | */ 1680 | life: number; 1681 | /** 1682 | * @type {Number} 1683 | */ 1684 | mass: number; 1685 | /** 1686 | * @type {Object} 1687 | */ 1688 | mirror: any; 1689 | /** 1690 | * @type {Vector} 1691 | */ 1692 | offset: Vector; 1693 | /** 1694 | * @type {Boolean} 1695 | */ 1696 | pixelPerfect: boolean; 1697 | /** 1698 | * @type {Boolean} 1699 | */ 1700 | removed: boolean; 1701 | /** 1702 | * @type {Number} 1703 | */ 1704 | rotationDrag: number; 1705 | /** 1706 | * @type {Boolean} 1707 | */ 1708 | rotationLock: boolean; 1709 | /** 1710 | * @type {Vector} 1711 | */ 1712 | scale: Vector; 1713 | /** 1714 | * @type {Number} 1715 | */ 1716 | shape: number; 1717 | /** 1718 | * @type {Boolean} 1719 | */ 1720 | sleeping: boolean; 1721 | /** 1722 | * @type {Color} 1723 | */ 1724 | stroke: Color; 1725 | /** 1726 | * @type {Number} 1727 | */ 1728 | strokeWeight: number; 1729 | /** 1730 | * @type {Number} 1731 | */ 1732 | text: number; 1733 | /** 1734 | * @type {Color} 1735 | */ 1736 | textColor: Color; 1737 | /** 1738 | * @type {String} 1739 | */ 1740 | tile: string; 1741 | /** 1742 | * @type {Number} 1743 | */ 1744 | tileSize: number; 1745 | /** 1746 | * @type {Boolean} 1747 | */ 1748 | visible: boolean; 1749 | /** 1750 | * @type {Number} 1751 | */ 1752 | w: number; 1753 | /** 1754 | * @type {Number} 1755 | */ 1756 | bearing: number; 1757 | /** 1758 | * @type {Number} 1759 | */ 1760 | d: number; 1761 | /** 1762 | * @type {Boolean} 1763 | */ 1764 | dynamic: boolean; 1765 | /** 1766 | * @type {String} 1767 | */ 1768 | heading: string; 1769 | /** 1770 | * @type {Boolean} 1771 | */ 1772 | kinematic: boolean; 1773 | /** 1774 | * @type {Boolean} 1775 | */ 1776 | resetAnimationsOnChange: boolean; 1777 | /** 1778 | * @type {Number} 1779 | */ 1780 | speed: number; 1781 | /** 1782 | * @type {Boolean} 1783 | */ 1784 | static: boolean; 1785 | /** 1786 | * Each group has a unique id number. Don't change it! 1787 | * Its useful for debugging. 1788 | * @type {Number} 1789 | */ 1790 | idNum: number; 1791 | /** 1792 | * Groups can have subgroups, which inherit the properties 1793 | * of their parent groups. 1794 | * @type {Group[]} 1795 | * @default [] 1796 | */ 1797 | subgroups: { 1798 | [x: string]: any; 1799 | }[]; 1800 | parent: any; 1801 | /** 1802 | * Keys are the animation label, values are Ani objects. 1803 | * @type {Anis} 1804 | */ 1805 | animations: Anis; 1806 | Sprite: typeof Sprite; 1807 | GroupSprite: typeof Sprite; 1808 | Group: typeof Group; 1809 | Subgroup: typeof Group; 1810 | mouse: { 1811 | presses: any; 1812 | pressing: any; 1813 | pressed: any; 1814 | holds: any; 1815 | holding: any; 1816 | held: any; 1817 | released: any; 1818 | hovers: any; 1819 | hovering: any; 1820 | hovered: any; 1821 | }; 1822 | /** 1823 | * autoCull is a property of the allSprites group only, 1824 | * that controls whether sprites are automatically removed 1825 | * when they are 10,000 pixels away from the camera. 1826 | * 1827 | * It only needs to be set to false once and then it will 1828 | * remain false for the rest of the sketch, unless changed. 1829 | * @type {Boolean} 1830 | */ 1831 | autoCull: boolean; 1832 | /** 1833 | * Alias for `push`. 1834 | * 1835 | * Adds a sprite to the end of the group. 1836 | */ 1837 | add: (...sprites: Sprite[]) => number; 1838 | /** 1839 | * Alias for group.includes 1840 | * 1841 | * Check if a sprite is in the group. 1842 | */ 1843 | contains: (searchElement: Sprite, fromIndex?: number) => boolean; 1844 | set ani(val: Ani); 1845 | /** 1846 | * Reference to the group's current animation. 1847 | * @type {Ani} 1848 | */ 1849 | get ani(): Ani; 1850 | set animation(val: Ani); 1851 | /** 1852 | * Reference to the group's current animation. 1853 | * @type {Ani} 1854 | */ 1855 | get animation(): Ani; 1856 | /** 1857 | * The group's animations. 1858 | * @type {Anis} 1859 | */ 1860 | get anis(): Anis; 1861 | set img(val: new (width?: number, height?: number) => HTMLImageElement); 1862 | /** 1863 | * Alias for `group.image`. 1864 | * @type {Image} 1865 | */ 1866 | get img(): new (width?: number, height?: number) => HTMLImageElement; 1867 | set image(img: new (width?: number, height?: number) => HTMLImageElement); 1868 | /** 1869 | * The group's image. 1870 | * @type {Image} 1871 | */ 1872 | get image(): new (width?: number, height?: number) => HTMLImageElement; 1873 | set amount(val: number); 1874 | /** 1875 | * Depending on the value that the amount property is set to, the group will 1876 | * either add or remove sprites. 1877 | * @type {Number} 1878 | */ 1879 | get amount(): number; 1880 | set diameter(val: number); 1881 | /** 1882 | * @type {Number} 1883 | */ 1884 | get diameter(): number; 1885 | set width(val: number); 1886 | /** 1887 | * @type {Number} 1888 | */ 1889 | get width(): number; 1890 | set height(val: number); 1891 | /** 1892 | * @type {Number} 1893 | */ 1894 | get height(): number; 1895 | set velocity(val: Vector); 1896 | /** 1897 | * The sprite's velocity vector {x, y} 1898 | * @type {Vector} 1899 | * @default {x: 0, y: 0} 1900 | */ 1901 | get velocity(): Vector; 1902 | centroid: { 1903 | x: number; 1904 | y: number; 1905 | }; 1906 | collide(target: any, callback: any): boolean; 1907 | /** 1908 | * Returns true on the first frame that the group collides with the 1909 | * target group. 1910 | * 1911 | * Custom collision event handling can be done by using this function 1912 | * in an if statement or adding a callback as the second parameter. 1913 | * 1914 | * @param {Group} target 1915 | * @param {Function} [callback] 1916 | */ 1917 | collides(target: Group, callback?: Function): boolean; 1918 | /** 1919 | * Returns the amount of frames that the group has been colliding 1920 | * with the target group for, which is a truthy value. Returns 0 if 1921 | * the group is not colliding with the target group. 1922 | * 1923 | * @param {Group} target 1924 | * @param {Function} [callback] 1925 | * @return {Number} frames 1926 | */ 1927 | colliding(target: Group, callback?: Function): number; 1928 | /** 1929 | * Returns true on the first frame that the group no longer overlaps 1930 | * with the target group. 1931 | * 1932 | * @param {Group} target 1933 | * @param {Function} [callback] 1934 | * @return {Boolean} 1935 | */ 1936 | collided(target: Group, callback?: Function): boolean; 1937 | overlap(target: any, callback: any): boolean; 1938 | /** 1939 | * Returns true on the first frame that the group overlaps with the 1940 | * target group. 1941 | * 1942 | * Custom overlap event handling can be done by using this function 1943 | * in an if statement or adding a callback as the second parameter. 1944 | * 1945 | * @param {Group} target 1946 | * @param {Function} [callback] 1947 | */ 1948 | overlaps(target: Group, callback?: Function): boolean; 1949 | /** 1950 | * Returns the amount of frames that the group has been overlapping 1951 | * with the target group for, which is a truthy value. Returns 0 if 1952 | * the group is not overlapping with the target group. 1953 | * 1954 | * @param {Group} target 1955 | * @param {Function} [callback] 1956 | * @return {Number} frames 1957 | */ 1958 | overlapping(target: Group, callback?: Function): number; 1959 | /** 1960 | * Returns true on the first frame that the group no longer overlaps 1961 | * with the target group. 1962 | * 1963 | * @param {Group} target 1964 | * @param {Function} [callback] 1965 | * @return {Boolean} 1966 | */ 1967 | overlapped(target: Group, callback?: Function): boolean; 1968 | /** 1969 | */ 1970 | applyForce(...args: any[]): void; 1971 | /** 1972 | */ 1973 | applyForceScaled(...args: any[]): void; 1974 | /** 1975 | */ 1976 | attractTo(...args: any[]): void; 1977 | /** 1978 | */ 1979 | applyTorque(...args: any[]): void; 1980 | /** 1981 | */ 1982 | move(distance: any, direction: any, speed: any): Promise; 1983 | /** 1984 | */ 1985 | moveTo(x: any, y: any, speed: any): Promise; 1986 | /** 1987 | */ 1988 | moveTowards(x: any, y: any, tracking: any): void; 1989 | /** 1990 | */ 1991 | moveAway(x: any, y: any, repel: any): void; 1992 | /** 1993 | */ 1994 | repelFrom(...args: any[]): void; 1995 | /** 1996 | * Alias for group.length 1997 | * @deprecated 1998 | */ 1999 | size(): number; 2000 | /** 2001 | * Remove sprites that go outside the given culling boundary 2002 | * relative to the camera. Sprites with chain colliders can not be culled. 2003 | * 2004 | * Can also be used with a uniform size for all boundaries, see example. 2005 | * 2006 | * @param {Number} top - the distance that sprites can move below the canvas before they are removed 2007 | * @param {Number} bottom - the distance that sprites can move below the canvas before they are removed 2008 | * @param {Number} left - the distance that sprites can move beyond the left side of the canvas before they are removed 2009 | * @param {Number} right - the distance that sprites can move beyond the right side of the canvas before they are removed 2010 | * @param {Function} [cb] - the function to be run when a sprite is culled, 2011 | * it's given the sprite being culled, if no callback is given then the 2012 | * sprite is removed 2013 | * @return {Number} the number of sprites culled 2014 | * @example 2015 | * // alternate uniform size syntax 2016 | * group.cull(100, callback); 2017 | */ 2018 | cull(top: number, bottom: number, left: number, right: number, cb?: Function): number; 2019 | /** 2020 | * If no input is given to this function, the group itself will be 2021 | * marked as removed and deleted from p5play's internal memory, the 2022 | * `p5play.groups` array. Every sprite in the group will be removed 2023 | * from the world and every other group they belong to. 2024 | * 2025 | * Groups should not be used after they are removed. 2026 | * 2027 | * If this function receives as input an index of a sprite in the 2028 | * group or the sprite itself, it will remove that sprite from 2029 | * this group and its super groups (if any), but NOT from the world. 2030 | * 2031 | * To remove a sprite from the world and every group it belongs to, 2032 | * use `sprite.remove()` instead. 2033 | * 2034 | * @param {Sprite|Number} item - the sprite to be removed or its index 2035 | * @return {Sprite} the removed sprite or undefined if the specified sprite was not found 2036 | */ 2037 | remove(item: Sprite | number): Sprite; 2038 | /** 2039 | * Using `group.remove` instead is recommended because it's easier to use, 2040 | * and it uses this function internally. 2041 | * 2042 | * Similar to `Array.splice` except it does not accept adding sprites, 2043 | * third parameters and beyond are ignored. 2044 | * 2045 | * This function also removes the group and its super-groups from the 2046 | * sprites' groups array. 2047 | * 2048 | * @param {Number} idx - index 2049 | * @param {Number} amount - number of sprites to remove 2050 | * @return {Sprite[]} the removed sprites 2051 | */ 2052 | splice(idx: number, amount: number): Sprite[]; 2053 | /** 2054 | * Not supported! 2055 | * @return {Number} the new length of the group 2056 | */ 2057 | unshift(): number; 2058 | /** 2059 | * Removes all the sprites in the group from the world and 2060 | * every other group they belong to. 2061 | * 2062 | * Does not delete the group itself. 2063 | */ 2064 | removeAll(): void; 2065 | /** 2066 | * Updates all the sprites in the group. 2067 | */ 2068 | update(): void; 2069 | /** 2070 | * Draws all the sprites in the group in ascending order 2071 | * by `sprite.layer`. 2072 | */ 2073 | draw(): void; 2074 | /** 2075 | * Runs every group sprite's post draw function. 2076 | */ 2077 | postDraw(): void; 2078 | } 2079 | /** 2080 | * @class 2081 | * @extends planck.World 2082 | */ 2083 | class World { 2084 | mod: {}; 2085 | /** 2086 | * Changes the world's origin point, 2087 | * where (0, 0) is on the canvas. 2088 | * @type {Object} 2089 | * @property {Number} x 2090 | * @property {Number} y 2091 | * @default { x: 0, y: 0 } 2092 | */ 2093 | origin: any; 2094 | contacts: any[]; 2095 | /** 2096 | * @type {Number} 2097 | * @default 8 2098 | */ 2099 | velocityIterations: number; 2100 | /** 2101 | * @type {Number} 2102 | * @default 3 2103 | */ 2104 | positionIterations: number; 2105 | set velocityThreshold(val: number); 2106 | /** 2107 | * The lowest velocity an object can have before it is considered 2108 | * to be at rest. 2109 | * 2110 | * Adjust the velocity threshold to allow for slow moving objects 2111 | * but don't have it be too low, or else objects will never sleep, 2112 | * which will hurt performance. 2113 | * 2114 | * @type {Number} 2115 | * @default 0.19 2116 | */ 2117 | get velocityThreshold(): number; 2118 | /** 2119 | * The time elapsed in the physics simulation in seconds. 2120 | * @type {Number} 2121 | */ 2122 | physicsTime: number; 2123 | /** 2124 | * Represents the size of a meter in pixels. 2125 | * 2126 | * Adjusting this property changes the simulated scale of the physics world. 2127 | * For optimal results, it should be set such that sprites are between 2128 | * 0.1 and 10 meters in size in the physics simulation. 2129 | * 2130 | * The default value is 60, which means that your sprites should optimally 2131 | * be between 6 and 600 pixels in size. 2132 | * @type {Number} 2133 | * @default 60 2134 | */ 2135 | meterSize: number; 2136 | /** 2137 | * The sprite the mouse is hovering over. 2138 | * 2139 | * If the mouse is hovering over several sprites, the mouse 2140 | * sprite will be the one with the highest layer value. 2141 | * @type {Sprite} 2142 | * @default null 2143 | */ 2144 | mouseSprite: Sprite; 2145 | /** 2146 | * The sprite(s) that the mouse is hovering over. 2147 | * @type {Sprite[]} 2148 | * @default [] 2149 | */ 2150 | mouseSprites: Sprite[]; 2151 | /** 2152 | * @type {Boolean} 2153 | * @default true 2154 | */ 2155 | autoStep: boolean; 2156 | step: (timeStep?: number, velocityIterations?: number, positionIterations?: number) => void; 2157 | steppedEvent: Event; 2158 | set gravity(val: any); 2159 | /** 2160 | * Gravity force vector that affects all dynamic physics colliders. 2161 | * @type {Object} 2162 | * @property {Number} x 2163 | * @property {Number} y 2164 | * @default { x: 0, y: 0 } 2165 | */ 2166 | get gravity(): any; 2167 | set timeScale(val: number); 2168 | /** 2169 | * A time scale of 1.0 represents real time. 2170 | * Accepts decimal values between 0 and 2. 2171 | * @type {Number} 2172 | * @default 1.0 2173 | */ 2174 | get timeScale(): number; 2175 | set updateRate(val: number); 2176 | /** 2177 | * The fixed update rate of the physics simulation in hertz. 2178 | * 2179 | * The time step, the amount of time that passes during a 2180 | * physics update, is calculated to be: 1 / updateRate * timeScale 2181 | * 2182 | * Setting the update rate to a value lower than 50hz is not 2183 | * recommended, as simulation quality will degrade. 2184 | * @type {Number} 2185 | * @default 60 2186 | */ 2187 | get updateRate(): number; 2188 | /** 2189 | * Performs a physics simulation step that advances all sprites' 2190 | * forward in time by 1/60th of a second if no timeStep is given. 2191 | * 2192 | * This function is automatically called at the end of the draw 2193 | * loop, unless it was already called inside the draw loop. 2194 | * 2195 | * Setting the timeStep below 1/50th of a second will 2196 | * significantly degrade simulation quality, without improving 2197 | * performance. Decreasing `velocityIterations` and 2198 | * `positionIterations` will improve performance but decrease 2199 | * simulation quality. 2200 | * 2201 | * @param {Number} [timeStep] - time step in seconds 2202 | * @param {Number} [velocityIterations] - 8 by default 2203 | * @param {Number} [positionIterations] - 3 by default 2204 | */ 2205 | physicsUpdate(timeStep?: number, velocityIterations?: number, positionIterations?: number): void; 2206 | /** 2207 | * Experimental! 2208 | * 2209 | * Visually moves all sprites forward in time by the given 2210 | * time step, based on their current velocity vector and 2211 | * rotation speed. 2212 | * 2213 | * Does not perform any physics calculations. 2214 | * 2215 | * This function may be useful for making extrapolated frames 2216 | * between physics steps, if a frame rate of 100hz or more 2217 | * is desired. 2218 | * @param {Number} [timeStep] - time step in seconds 2219 | */ 2220 | extrapolationUpdate(timeStep?: number): void; 2221 | /** 2222 | * The real time in seconds since the world was created, including 2223 | * time spent paused. 2224 | * @type {Number} 2225 | */ 2226 | get realTime(): number; 2227 | /** 2228 | * Returns the sprites at a position, ordered by layer. 2229 | * 2230 | * Sprites must have a physics body to be detected. 2231 | * @param {Number} x - x coordinate or position object 2232 | * @param {Number} y 2233 | * @param {Group} [group] - limit results to a specific group, 2234 | * allSprites by default 2235 | * @param {Boolean} [cameraActiveWhenDrawn] - limit results to 2236 | * sprites drawn when the camera was active, true by default 2237 | * @returns {Sprite[]} an array of sprites 2238 | */ 2239 | getSpritesAt(x: number, y: number, group?: Group, cameraActiveWhenDrawn?: boolean): Sprite[]; 2240 | /** 2241 | * Returns the sprite at the specified position 2242 | * on the top most layer, drawn when the camera was on. 2243 | * 2244 | * The sprite must have a physics body to be detected. 2245 | * @param {Number} x 2246 | * @param {Number} y 2247 | * @param {Group} [group] - the group to search 2248 | * @returns {Sprite} a sprite 2249 | */ 2250 | getSpriteAt(x: number, y: number, group?: Group): Sprite; 2251 | getMouseSprites(): Sprite[]; 2252 | set allowSleeping(val: boolean); 2253 | /** 2254 | * "Sleeping" sprites get temporarily ignored during physics 2255 | * simulation. A sprite starts "sleeping" when it stops moving and 2256 | * doesn't collide with anything that it wasn't already touching. 2257 | * 2258 | * This is an important performance optimization that you probably 2259 | * shouldn't disable for every sprite in the world. 2260 | * @type {Boolean} 2261 | * @default true 2262 | */ 2263 | get allowSleeping(): boolean; 2264 | /** 2265 | * Finds the first sprite (with a physics body) that 2266 | * intersects a ray (line), excluding any sprites that intersect 2267 | * with the starting point. 2268 | * 2269 | * This function can also be given start and end points. 2270 | * @param {Object} startPos - starting position of the ray cast 2271 | * @param {Number} direction - direction of the ray 2272 | * @param {Number} maxDistance - max distance the ray should check 2273 | * @returns {Sprite} The first sprite the ray hits or undefined 2274 | */ 2275 | rayCast(startPos: any, direction: number, maxDistance: number): Sprite; 2276 | /** 2277 | * Finds sprites (with physics bodies) that intersect 2278 | * a line (ray), excluding any sprites that intersect the 2279 | * starting point. 2280 | * 2281 | * This function can also be given start and end points. 2282 | * @param {Object} startPos - starting position of the ray cast 2283 | * @param {Number} direction - direction of the ray 2284 | * @param {Number} maxDistance - max distance the ray should check 2285 | * @param {Function} [limiter] - limiter function that's run each time the ray intersects a sprite, return true to stop the ray 2286 | * @returns {Sprite[]} An array of sprites that the ray cast hit, sorted by distance. The sprite closest to the starting point will be at index 0. 2287 | */ 2288 | rayCastAll(startPos: any, direction: number, maxDistance: number, limiter?: Function, ...args: any[]): Sprite[]; 2289 | } 2290 | /** 2291 | * @class 2292 | */ 2293 | class Camera { 2294 | /** 2295 | * Read only. True if the camera is active. 2296 | * Use camera.on() to activate the camera. 2297 | * @type {Boolean} 2298 | * @default false 2299 | */ 2300 | isActive: boolean; 2301 | bound: { 2302 | min: { 2303 | x: number; 2304 | y: number; 2305 | }; 2306 | max: { 2307 | x: number; 2308 | y: number; 2309 | }; 2310 | }; 2311 | set pos(val: any); 2312 | /** 2313 | * The camera's position. {x, y} 2314 | * @type {Object} 2315 | */ 2316 | get pos(): any; 2317 | set x(val: number); 2318 | /** 2319 | * The camera x position. 2320 | * @type {Number} 2321 | */ 2322 | get x(): number; 2323 | set y(val: number); 2324 | /** 2325 | * The camera y position. 2326 | * @type {Number} 2327 | */ 2328 | get y(): number; 2329 | set position(val: any); 2330 | /** 2331 | * The camera's position. Alias for pos. 2332 | * @type {Object} 2333 | */ 2334 | get position(): any; 2335 | /** 2336 | * Moves the camera to a position. Similar to `sprite.moveTo`. 2337 | * 2338 | * @param {Number} x 2339 | * @param {Number} y 2340 | * @param {Number} speed 2341 | * @returns {Promise} resolves true when the camera reaches the target position 2342 | */ 2343 | moveTo(x: number, y: number, speed: number): Promise; 2344 | set zoom(val: number); 2345 | /** 2346 | * Camera zoom. 2347 | * 2348 | * A scale of 1 will be the normal size. Setting it to 2 2349 | * will make everything appear twice as big. .5 will make 2350 | * everything look half size. 2351 | * @type {Number} 2352 | * @default 1 2353 | */ 2354 | get zoom(): number; 2355 | /** 2356 | * Zoom the camera at a given speed. 2357 | * 2358 | * @param {Number} target - The target zoom 2359 | * @param {Number} speed - The amount of zoom per frame 2360 | * @returns {Promise} resolves true when the camera reaches the target zoom 2361 | */ 2362 | zoomTo(target: number, speed: number): Promise; 2363 | /** 2364 | * Activates the camera. 2365 | * 2366 | * The canvas will be drawn according to the camera position and scale until 2367 | * camera.off() is called. 2368 | */ 2369 | on(): void; 2370 | /** 2371 | * Deactivates the camera. 2372 | * 2373 | * The canvas will be drawn normally, ignoring the camera's position 2374 | * and scale until camera.on() is called. 2375 | */ 2376 | off(): void; 2377 | } 2378 | /** 2379 | * @class 2380 | */ 2381 | class Tiles { 2382 | /** 2383 | * 2384 | * Look at the Tiles reference pages before reading these docs. 2385 | * 2386 | * 2387 | * Returns a group containing all the tile sprites created by 2388 | * the `Tiles` constructor. 2389 | * 2390 | * @param {String} tiles 2391 | * @param {Number} x 2392 | * @param {Number} y 2393 | * @param {Number} w 2394 | * @param {Number} h 2395 | */ 2396 | constructor(tiles: string, x: number, y: number, w: number, h: number); 2397 | } 2398 | function createTiles(tiles: any, x: any, y: any, w: any, h: any): any; 2399 | /** 2400 | * @class 2401 | */ 2402 | class Joint { 2403 | /** 2404 | * Using this Joint class directly is not recommended, but 2405 | * if it is used a GlueJoint will be created. 2406 | * 2407 | * It's better to use a specific joint class constructor: 2408 | * 2409 | * GlueJoint, DistanceJoint, WheelJoint, HingeJoint, 2410 | * SliderJoint, RopeJoint, or GrabberJoint. 2411 | * 2412 | * All other joint classes extend this class. Joint type 2413 | * can not be changed after a joint is created. 2414 | * 2415 | * @param {Sprite} spriteA 2416 | * @param {Sprite} spriteB 2417 | * @param {String} [type] 2418 | */ 2419 | constructor(spriteA: Sprite, spriteB: Sprite, type?: string); 2420 | /** 2421 | * The first sprite in the joint. 2422 | * @type {Sprite} 2423 | */ 2424 | spriteA: Sprite; 2425 | /** 2426 | * The second sprite in the joint. 2427 | * @type {Sprite} 2428 | */ 2429 | spriteB: Sprite; 2430 | /** 2431 | * Read only. The type of joint. Can be one of: 2432 | * 2433 | * "glue", "distance", "wheel", "hinge", "slider", or "rope". 2434 | * 2435 | * Can't be changed after the joint is created. 2436 | * @type {String} 2437 | */ 2438 | type: string; 2439 | /** 2440 | * Determines whether to draw the joint if spriteA 2441 | * or spriteB is drawn. 2442 | * @type {Boolean} 2443 | * @default true 2444 | */ 2445 | visible: boolean; 2446 | set draw(val: Function); 2447 | /** 2448 | * Function that draws the joint. Can be overridden by the user. 2449 | * @type {Function} 2450 | * @param {Number} xA 2451 | * @param {Number} yA 2452 | * @param {Number} [xB] 2453 | * @param {Number} [yB] 2454 | */ 2455 | get draw(): Function; 2456 | set offsetA(val: Vector); 2457 | /** 2458 | * Offset to the joint's anchorA position from the center of spriteA. 2459 | * 2460 | * Only distance and hinge joints have an offsetA. 2461 | * @type {Vector} 2462 | * @default {x: 0, y: 0} 2463 | */ 2464 | get offsetA(): Vector; 2465 | set offsetB(val: Vector); 2466 | /** 2467 | * Offset to the joint's anchorB position from the center of spriteB. 2468 | * 2469 | * Only distance, hinge, and wheel joints have an offsetB. 2470 | * @type {Vector} 2471 | * @default {x: 0, y: 0} 2472 | */ 2473 | get offsetB(): Vector; 2474 | set springiness(val: number); 2475 | /** 2476 | * The springiness of the joint, a 0-1 ratio. 2477 | * 2478 | * 0.0 makes the joint completely rigid, and 2479 | * 1.0 turns the joint into a super loose spring, 2480 | * like a broken slinky that was overextended. 2481 | * 2482 | * Springiness is a user friendly wrapper around Box2D's spring 2483 | * frequency joint parameter. It's 0-1 ratio is piecewise mapped 2484 | * to the range of 30-0.2hz, except 0 remains 0. 2485 | * 2486 | * 0.0 -> 0hz (perfectly rigid) 2487 | * >0.0-0.1 -> 30hz-4hz (steel rod) 2488 | * 0.1-0.5 -> 4hz-2.5hz (tight spring) 2489 | * 0.5-0.8 -> 2.5hz-1hz (bouncy spring) 2490 | * 0.8-0.9 -> 1hz-0.5hz (slinky) 2491 | * 0.9-1.0 -> 0.5hz-0.2hz (bungee cord) 2492 | * @type {Number} 2493 | * @default 0.0 2494 | */ 2495 | get springiness(): number; 2496 | set damping(val: number); 2497 | /** 2498 | * Damping only effects joint's that have a 2499 | * springiness greater than 0. 2500 | * 2501 | * Damping is a 0-1 ratio describing how quickly the joint loses 2502 | * vibrational energy. 2503 | * 2504 | * 0.0 lets the joint continue to spring up and down very easily. 2505 | * 1.0 makes the joint lose vibrational energy immediately, 2506 | * making the joint completely rigid, regardless of its springiness. 2507 | * @type {Number} 2508 | * @default 0.0 2509 | */ 2510 | get damping(): number; 2511 | set speed(val: number); 2512 | /** 2513 | * The current speed of the joint's motor. 2514 | * @type {Number} 2515 | * @default 0 2516 | */ 2517 | get speed(): number; 2518 | get motorSpeed(): any; 2519 | set enableMotor(val: boolean); 2520 | /** 2521 | * Enable or disable the joint's motor. 2522 | * Disabling the motor is like putting a 2523 | * car in neutral. 2524 | * @type {Boolean} 2525 | */ 2526 | get enableMotor(): boolean; 2527 | set maxPower(val: number); 2528 | /** 2529 | * Max power is how the amount of torque a joint motor can exert 2530 | * around its axis of rotation. 2531 | * @type {Number} 2532 | * @default 0 2533 | */ 2534 | get maxPower(): number; 2535 | /** 2536 | * Read only. The joint's current power, the amount of torque 2537 | * being applied on the joint's axis of rotation. 2538 | * @type {Number} 2539 | * @default 0 2540 | */ 2541 | get power(): number; 2542 | set collideConnected(val: boolean); 2543 | /** 2544 | * Set to true if you want the joint's sprites to collide with 2545 | * each other. 2546 | * @type {Boolean} 2547 | * @default false 2548 | */ 2549 | get collideConnected(): boolean; 2550 | /** 2551 | * Read only. The joint's reaction force. 2552 | */ 2553 | get reactionForce(): any; 2554 | /** 2555 | * Read only. The joint's reaction torque. 2556 | */ 2557 | get reactionTorque(): any; 2558 | /** 2559 | * Removes the joint from the world and from each of the 2560 | * sprites' joints arrays. 2561 | */ 2562 | remove(): void; 2563 | } 2564 | /** 2565 | * @class 2566 | * @extends Joint 2567 | */ 2568 | class GlueJoint extends Joint { 2569 | /** 2570 | * Glue joints are used to glue two sprites together. 2571 | * 2572 | * @param {Sprite} spriteA 2573 | * @param {Sprite} spriteB 2574 | */ 2575 | constructor(spriteA: Sprite, spriteB: Sprite, ...args: any[]); 2576 | } 2577 | /** 2578 | * @class 2579 | * @extends Joint 2580 | */ 2581 | class DistanceJoint extends Joint { 2582 | /** 2583 | * Distance joints are used to constrain the distance 2584 | * between two sprites. 2585 | * 2586 | * @param {Sprite} spriteA 2587 | * @param {Sprite} spriteB 2588 | */ 2589 | constructor(spriteA: Sprite, spriteB: Sprite, ...args: any[]); 2590 | } 2591 | /** 2592 | * @class 2593 | * @extends Joint 2594 | */ 2595 | class WheelJoint extends Joint { 2596 | /** 2597 | * Wheel joints can be used to create vehicles! 2598 | * 2599 | * By default the motor is disabled, angle is 90 degrees, 2600 | * maxPower is 1000, springiness is 0.1, and damping is 0.7. 2601 | * 2602 | * @param {Sprite} spriteA - the vehicle body 2603 | * @param {Sprite} spriteB - the wheel 2604 | */ 2605 | constructor(spriteA: Sprite, spriteB: Sprite, ...args: any[]); 2606 | set angle(val: number); 2607 | /** 2608 | * The angle at which the wheel is attached to the vehicle body. 2609 | * 2610 | * The default is 90 degrees or PI/2 radians, which is vertical. 2611 | * @type {Number} 2612 | * @default 90 2613 | */ 2614 | get angle(): number; 2615 | } 2616 | /** 2617 | * @class 2618 | * @extends Joint 2619 | */ 2620 | class HingeJoint extends Joint { 2621 | /** 2622 | * Hinge joints attach two sprites together at a pivot point, 2623 | * constraining them to rotate around this point, like a hinge. 2624 | * 2625 | * A known as a revolute joint. 2626 | * 2627 | * @param {Sprite} spriteA 2628 | * @param {Sprite} spriteB 2629 | */ 2630 | constructor(spriteA: Sprite, spriteB: Sprite, ...args: any[]); 2631 | set range(val: number); 2632 | /** 2633 | * The joint's range of rotation. Setting the range 2634 | * changes the joint's upper and lower limits. 2635 | * @type {Number} 2636 | * @default undefined 2637 | */ 2638 | get range(): number; 2639 | set upperLimit(val: number); 2640 | /** 2641 | * The upper limit of rotation. 2642 | * @type {Number} 2643 | * @default undefined 2644 | */ 2645 | get upperLimit(): number; 2646 | set lowerLimit(val: number); 2647 | /** 2648 | * The lower limit of rotation. 2649 | * @type {Number} 2650 | * @default undefined 2651 | */ 2652 | get lowerLimit(): number; 2653 | /** 2654 | * Read only. The joint's current angle of rotation. 2655 | * @type {Number} 2656 | * @default 0 2657 | */ 2658 | get angle(): number; 2659 | } 2660 | /** 2661 | * @class 2662 | * @extends Joint 2663 | */ 2664 | class SliderJoint extends Joint { 2665 | /** 2666 | * A slider joint constrains the motion of two sprites to sliding 2667 | * along a common axis, without rotation. 2668 | * 2669 | * Also known as a prismatic joint. 2670 | * 2671 | * @param {Sprite} spriteA 2672 | * @param {Sprite} spriteB 2673 | */ 2674 | constructor(spriteA: Sprite, spriteB: Sprite, ...args: any[]); 2675 | set angle(val: number); 2676 | /** 2677 | * The angle of the joint's axis which its sprites slide along. 2678 | * @type {Number} 2679 | * @default 0 2680 | */ 2681 | get angle(): number; 2682 | set range(val: number); 2683 | /** 2684 | * The joint's range of translation. Setting the range 2685 | * changes the joint's upper and lower limits. 2686 | * @type {Number} 2687 | * @default undefined 2688 | */ 2689 | get range(): number; 2690 | set upperLimit(val: number); 2691 | /** 2692 | * The mathematical upper (not positionally higher) 2693 | * limit of translation. 2694 | * @type {Number} 2695 | * @default undefined 2696 | */ 2697 | get upperLimit(): number; 2698 | set lowerLimit(val: number); 2699 | /** 2700 | * The mathematical lower (not positionally lower) 2701 | * limit of translation. 2702 | * @type {Number} 2703 | * @default undefined 2704 | */ 2705 | get lowerLimit(): number; 2706 | } 2707 | /** 2708 | * @class 2709 | * @extends Joint 2710 | */ 2711 | class RopeJoint extends Joint { 2712 | /** 2713 | * A Rope joint prevents two sprites from going further 2714 | * than a certain distance from each other, which is 2715 | * defined by the max length of the rope, but they do allow 2716 | * the sprites to get closer together. 2717 | * 2718 | * @param {Sprite} spriteA 2719 | * @param {Sprite} spriteB 2720 | */ 2721 | constructor(spriteA: Sprite, spriteB: Sprite, ...args: any[]); 2722 | set maxLength(val: number); 2723 | /** 2724 | * The maximum length of the rope. 2725 | */ 2726 | get maxLength(): number; 2727 | } 2728 | /** 2729 | * @class 2730 | * @extends Joint 2731 | */ 2732 | class GrabberJoint extends Joint { 2733 | /** 2734 | * A Grabber joint enables you to grab sprites and move them with 2735 | * a max force towards a target position. 2736 | * 2737 | * @param {Sprite} sprite - the sprite to grab 2738 | */ 2739 | constructor(sprite: Sprite); 2740 | set target(pos: any); 2741 | /** 2742 | * The target position of the joint that the sprite will be 2743 | * moved towards. Must be an object with x and y properties. 2744 | * @type {Object} 2745 | */ 2746 | get target(): any; 2747 | set maxForce(val: number); 2748 | /** 2749 | * The maximum force that the joint can exert on the sprite. 2750 | * @type {Number} 2751 | * @default 1000 2752 | */ 2753 | get maxForce(): number; 2754 | } 2755 | class Scale { 2756 | valueOf(): any; 2757 | } 2758 | function colorPal(c: string, palette: number | any): string; 2759 | function EmojiImage(emoji: string, textSize: number): new (width?: number, height?: number) => HTMLImageElement; 2760 | function spriteArt(txt: string, scale: number, palette: number | any): new (width?: number, height?: number) => HTMLImageElement; 2761 | function createSprite(...args: any[]): Sprite; 2762 | function createGroup(...args: any[]): Group; 2763 | function loadAnimation(...args: any[]): Ani; 2764 | function loadAni(...args: any[]): Ani; 2765 | function animation(ani: Ani, x: number, y: number, r: number, sX: number, sY: number): void; 2766 | function delay(milliseconds: any): Promise; 2767 | function sleep(milliseconds: any): Promise; 2768 | function play(sound: any): Promise; 2769 | function createCanvas(...args: any[]): Canvas; 2770 | /** 2771 | * @class 2772 | */ 2773 | class Canvas { 2774 | /** 2775 | * p5play adds some extra functionality to the `createCanvas` 2776 | * function. See the examples below. 2777 | * 2778 | * Creating a canvas in p5play disables the browser's default 2779 | * keydown responses for the slash, space, and arrow keys to 2780 | * prevent page scrolling which is disruptive to gameplay. 2781 | * 2782 | * For an easy way to scale the canvas or make it pixelated, use 2783 | * the `displayMode` function. 2784 | * 2785 | * Only q5.js has support for canvas options. 2786 | * 2787 | * @param {Number} [width] 2788 | * @param {Number} [height] 2789 | * @param {Object} [options] - canvas options or renderer 2790 | * @returns HTML5 canvas element 2791 | * @example 2792 | * // fills the window 2793 | * new Canvas(); 2794 | * // max 16:9 aspect ratio dimensions that will fit the window 2795 | * new Canvas('16:9'); 2796 | * // 800x600 pixels 2797 | * new Canvas(800, 600); 2798 | */ 2799 | constructor(width?: number, height?: number, options?: any); 2800 | /** 2801 | * The width of the canvas. 2802 | * @type {Number} 2803 | * @default 100 2804 | */ 2805 | w: number; 2806 | /** 2807 | * The width of the canvas. 2808 | * @type {Number} 2809 | * @default 100 2810 | */ 2811 | width: number; 2812 | /** 2813 | * The height of the canvas. 2814 | * @type {Number} 2815 | * @default 100 2816 | */ 2817 | h: number; 2818 | /** 2819 | * The height of the canvas. 2820 | * @type {Number} 2821 | * @default 100 2822 | */ 2823 | height: number; 2824 | /** 2825 | * Half the width of the canvas. 2826 | * @type {Number} 2827 | * @default 50 2828 | */ 2829 | hw: number; 2830 | /** 2831 | * Half the height of the canvas. 2832 | * @type {Number} 2833 | * @default 50 2834 | */ 2835 | hh: number; 2836 | /** 2837 | * Absolute position of the mouse on the canvas, not relative 2838 | * to the camera. Same values as `mouseX` and `mouseY`. 2839 | * @type {Object} 2840 | * @property {Number} x 2841 | * @property {Number} y 2842 | */ 2843 | mouse: any; 2844 | /** 2845 | * Resizes the canvas, the world, and centers the camera. 2846 | * 2847 | * Visually the canvas will shrink or extend to the new size. Sprites 2848 | * will not change position. 2849 | * 2850 | * If you would prefer to keep the camera focused on the same area, 2851 | * then you must manually adjust the camera position after calling 2852 | * this function. 2853 | * 2854 | * @param {Number} w - the new width of the canvas 2855 | * @param {Number} h - the new height of the canvas 2856 | */ 2857 | resize(): void; 2858 | /** 2859 | * Saves the current canvas as an image file. 2860 | * @param {String} file - the name of the image 2861 | */ 2862 | save(): void; 2863 | } 2864 | 2865 | /** 2866 | * A FriendlyError is a custom error class that extends the native JS 2867 | * Error class. It's used internally by p5play to make error messages 2868 | * more helpful. 2869 | * 2870 | * @private 2871 | * @param {String} func - the name of the function the error was thrown in 2872 | * @param {Number} errorNum - the error's code number 2873 | * @param {Array} e - an array of values relevant to the error 2874 | */ 2875 | class FriendlyError extends Error { 2876 | constructor(func: any, errorNum: any, e: any); 2877 | } 2878 | var allSprites: Group; 2879 | var world: World; 2880 | var camera: Camera; 2881 | /** 2882 | * @class 2883 | */ 2884 | class InputDevice { 2885 | /** 2886 | * The amount of frames an input must be pressed to be considered held. 2887 | * @type {number} 2888 | * @default 12 2889 | */ 2890 | holdThreshold: number; 2891 | /** 2892 | * @param {string} inp 2893 | * @returns {boolean} true on the first frame that the user presses the input 2894 | */ 2895 | presses(inp: string): boolean; 2896 | /** 2897 | * @param {string} inp 2898 | * @returns {number} the amount of frames the user has been pressing the input 2899 | */ 2900 | pressing(inp: string): number; 2901 | /** 2902 | * Same as the `released` function, which is preferred. 2903 | * @param {string} inp 2904 | * @returns {boolean} true on the first frame that the user released the input 2905 | */ 2906 | pressed(inp: string): boolean; 2907 | /** 2908 | * @param {string} inp 2909 | * @returns {boolean} true on the first frame that the user holds the input 2910 | */ 2911 | holds(inp: string): boolean; 2912 | /** 2913 | * @param {string} inp 2914 | * @returns {number} the amount of frames the user has been holding the input 2915 | */ 2916 | holding(inp: string): number; 2917 | /** 2918 | * @param {string} inp 2919 | * @returns {boolean} true on the first frame that the user released a held input 2920 | */ 2921 | held(inp: string): boolean; 2922 | /** 2923 | * @param {string} inp 2924 | * @returns {boolean} true on the first frame that the user released the input 2925 | */ 2926 | released(inp: string): boolean; 2927 | releases(inp: any): boolean; 2928 | } 2929 | /** 2930 | * @class 2931 | * @extends InputDevice 2932 | */ 2933 | class _Mouse extends InputDevice { 2934 | /** 2935 | * The mouse's x position in the world. 2936 | * @type {Number} 2937 | */ 2938 | x: number; 2939 | /** 2940 | * The mouse's y position in the world. 2941 | * @type {Number} 2942 | */ 2943 | y: number; 2944 | /** 2945 | * The mouse's absolute position on the canvas. 2946 | * @type {object} 2947 | * @property {Number} x 2948 | * @property {Number} y 2949 | */ 2950 | canvasPos: object; 2951 | /** 2952 | * The mouse's left button. 2953 | * @type {Number} 2954 | */ 2955 | left: number; 2956 | /** 2957 | * The mouse's center button. 2958 | * @type {Number} 2959 | */ 2960 | center: number; 2961 | /** 2962 | * The mouse's right button. 2963 | * @type {Number} 2964 | */ 2965 | right: number; 2966 | /** 2967 | * Contains the drag status of each of the mouse's buttons. 2968 | * @type {object} 2969 | */ 2970 | drag: object; 2971 | /** 2972 | * True if the mouse is currently on the canvas. 2973 | * @type {boolean} 2974 | * @default false 2975 | */ 2976 | isOnCanvas: boolean; 2977 | /** 2978 | * True if the mouse has ever interacted with the canvas. 2979 | * @type {boolean} 2980 | * @default false 2981 | */ 2982 | isActive: boolean; 2983 | /** 2984 | * The mouse's position. 2985 | * @type {object} 2986 | */ 2987 | get pos(): object; 2988 | /** 2989 | * The mouse's position. Alias for pos. 2990 | * @type {object} 2991 | */ 2992 | get position(): object; 2993 | set cursor(val: string); 2994 | /** 2995 | * The mouse's CSS cursor style. 2996 | * @type {string} 2997 | * @default 'default' 2998 | */ 2999 | get cursor(): string; 3000 | set visible(val: boolean); 3001 | /** 3002 | * Controls whether the mouse is visible or not. 3003 | * @type {boolean} 3004 | * @default true 3005 | */ 3006 | get visible(): boolean; 3007 | /** 3008 | * @param {string} inp 3009 | * @returns {boolean} true on the first frame that the user moves the mouse while pressing the input 3010 | */ 3011 | drags(inp: string): boolean; 3012 | /** 3013 | * @param {string} inp 3014 | * @returns {number} the amount of frames the user has been moving the mouse while pressing the input 3015 | */ 3016 | dragging(inp: string): number; 3017 | /** 3018 | * @param {string} inp 3019 | * @returns {boolean} true on the first frame that the user releases the input after dragging the mouse 3020 | */ 3021 | dragged(inp: string): boolean; 3022 | } 3023 | var mouse: _Mouse; 3024 | /** 3025 | * @class 3026 | * @extends _Mouse 3027 | */ 3028 | class _SpriteMouse extends _Mouse { 3029 | hover: number; 3030 | /** 3031 | * @returns {boolean} true on the first frame that the mouse is over the sprite 3032 | */ 3033 | hovers(): boolean; 3034 | /** 3035 | * @returns {number} the amount of frames the mouse has been over the sprite 3036 | */ 3037 | hovering(): number; 3038 | /** 3039 | * @returns {boolean} true on the first frame that the mouse is no longer over the sprite 3040 | */ 3041 | hovered(): boolean; 3042 | } 3043 | /** 3044 | * @class 3045 | * @extends InputDevice 3046 | */ 3047 | class _Touch extends InputDevice { 3048 | /** 3049 | * 3050 | * Look at the Input reference pages before reading these docs. 3051 | * 3052 | * 3053 | * Used internally to create touch input objects in the `touches` array. 3054 | */ 3055 | constructor(touch: any); 3056 | /** 3057 | * The touch's x position in the world. 3058 | * @type {Number} 3059 | */ 3060 | x: number; 3061 | /** 3062 | * The touch's y position in the world. 3063 | * @type {Number} 3064 | */ 3065 | y: number; 3066 | /** 3067 | * The touch's unique identifier. 3068 | * @type {Number} 3069 | */ 3070 | id: number; 3071 | /** 3072 | * The amount of frames the user has been touching the screen. 3073 | * @type {Number} 3074 | */ 3075 | duration: number; 3076 | /** 3077 | * The amount of frames the user has been dragging on the screen. 3078 | * @type {Number} 3079 | */ 3080 | drag: number; 3081 | /** 3082 | * The touch's absolute position on the canvas. 3083 | * @type {Object} 3084 | * @property {Number} x 3085 | * @property {Number} y 3086 | */ 3087 | canvasPos: any; 3088 | force: any; 3089 | } 3090 | /** 3091 | * @class 3092 | * @extends InputDevice 3093 | */ 3094 | class _Keyboard extends InputDevice { 3095 | alt: number; 3096 | arrowUp: number; 3097 | arrowDown: number; 3098 | arrowLeft: number; 3099 | arrowRight: number; 3100 | backspace: number; 3101 | capsLock: number; 3102 | control: number; 3103 | enter: number; 3104 | meta: number; 3105 | shift: number; 3106 | tab: number; 3107 | set visible(v: boolean); 3108 | get visible(): boolean; 3109 | get cmd(): number; 3110 | get command(): number; 3111 | get ctrl(): number; 3112 | get space(): any; 3113 | get spacebar(): any; 3114 | get opt(): number; 3115 | get option(): number; 3116 | get win(): number; 3117 | get windows(): number; 3118 | } 3119 | var kb: _Keyboard; 3120 | var keyboard: _Keyboard; 3121 | /** 3122 | * @class 3123 | * @extends InputDevice 3124 | */ 3125 | class Contro extends InputDevice { 3126 | /** 3127 | * 3128 | * Look at the Input reference pages before reading these docs. 3129 | * 3130 | * 3131 | * Stores the input status of buttons, triggers, and sticks on 3132 | * game controllers. Used internally to create controller objects 3133 | * for the `contros` array (aka `controllers`). 3134 | * 3135 | * Can also be used to create a mock controller object. 3136 | * @param {Gamepad} gamepad - gamepad object or id string for a mock controller 3137 | */ 3138 | constructor(gp: any); 3139 | connected: boolean; 3140 | a: number; 3141 | b: number; 3142 | x: number; 3143 | y: number; 3144 | l: number; 3145 | r: number; 3146 | lt: number; 3147 | rt: number; 3148 | select: number; 3149 | start: number; 3150 | lsb: number; 3151 | rsb: number; 3152 | up: number; 3153 | down: number; 3154 | left: number; 3155 | right: number; 3156 | /** 3157 | * Has x and y properties with -1 to 1 values which 3158 | * represent the position of the left analog stick. 3159 | * 3160 | * {x: 0, y: 0} is the center position. 3161 | * @type {Object} 3162 | */ 3163 | leftStick: any; 3164 | /** 3165 | * Has x and y properties with -1 to 1 values which 3166 | * represent the position of the right analog stick. 3167 | * 3168 | * {x: 0, y: 0} is the center position. 3169 | * @type {Object} 3170 | */ 3171 | rightStick: any; 3172 | /** 3173 | * Analog value 0-1 of the left trigger. 3174 | * @default 0 3175 | */ 3176 | leftTrigger: number; 3177 | /** 3178 | * Analog value 0-1 of the right trigger. 3179 | * @default 0 3180 | */ 3181 | rightTrigger: number; 3182 | /** 3183 | * Button names are mapped to `gamepad.buttons` indices. 3184 | * @type {Object} 3185 | */ 3186 | buttonMapping: any; 3187 | /** 3188 | * Sticks and triggers are mapped to `gamepad.axes` indices. 3189 | * @type {Object} 3190 | */ 3191 | axeMapping: any; 3192 | /** 3193 | * If the controller is a mock controller. 3194 | * @type {Boolean} 3195 | */ 3196 | isMock: boolean; 3197 | gamepad: any; 3198 | id: any; 3199 | /** 3200 | * True if the controller has analog triggers. 3201 | * False if the controller has digital (button) triggers. 3202 | * @type {boolean} 3203 | */ 3204 | hasAnalogTriggers: boolean; 3205 | get cross(): number; 3206 | get circle(): number; 3207 | get square(): number; 3208 | get triangle(): number; 3209 | /** 3210 | * Alias for `leftStick`. 3211 | */ 3212 | get ls(): any; 3213 | /** 3214 | * Alias for `rightStick`. 3215 | */ 3216 | get rs(): any; 3217 | /** 3218 | * Alias for `l` (left button). 3219 | * `lb` is what the button is called on Xbox controllers. 3220 | */ 3221 | get lb(): number; 3222 | /** 3223 | * Alias for `r` (right button). 3224 | * `rb` is what the button is called on Xbox controllers. 3225 | */ 3226 | get rb(): number; 3227 | /** 3228 | * Alias for `l` (left button). 3229 | * `l1` is what the button is called on PlayStation controllers. 3230 | */ 3231 | get l1(): number; 3232 | /** 3233 | * Alias for `r` (right button). 3234 | * `r1` is what the button is called on PlayStation controllers. 3235 | */ 3236 | get r1(): number; 3237 | /** 3238 | * Alias for `lt` (digital left trigger). 3239 | * `zl` is what the button is called on Nintendo controllers. 3240 | */ 3241 | get zl(): number; 3242 | /** 3243 | * Alias for `rt` (digital right trigger). 3244 | * `zr` is what the button is called on Nintendo controllers. 3245 | */ 3246 | get zr(): number; 3247 | /** 3248 | * Alias for `leftTrigger` (analog left trigger). 3249 | * `l2` is what the trigger is called on PlayStation controllers. 3250 | */ 3251 | get l2(): number; 3252 | /** 3253 | * Alias for `rightTrigger` (analog right trigger). 3254 | * `r2` is what the trigger is called on PlayStation controllers. 3255 | */ 3256 | get r2(): number; 3257 | /** 3258 | * Verbose alias for `lsb`. 3259 | */ 3260 | get leftStickButton(): number; 3261 | /** 3262 | * Verbose alias for `rsb`. 3263 | */ 3264 | get rightStickButton(): number; 3265 | /** 3266 | * Alias for `lsb` (left stick button). 3267 | * `l3` is what the trigger is called on PlayStation controllers. 3268 | */ 3269 | get l3(): number; 3270 | /** 3271 | * Alias for `rsb` (right stick button). 3272 | * `r3` is what the trigger is called on PlayStation controllers. 3273 | */ 3274 | get r3(): number; 3275 | } 3276 | /** 3277 | * @class 3278 | * @extends Array 3279 | */ 3280 | class _Contros extends Array { 3281 | /** 3282 | * 3283 | * Look at the Input reference pages before reading these docs. 3284 | * 3285 | * 3286 | * Used internally to create the `contros` array (aka `controllers`) 3287 | * of `Contro` objects, which store the input status of buttons, 3288 | * triggers, and sticks on game controllers. 3289 | */ 3290 | constructor(); 3291 | /** 3292 | * Swap controller positions in this controllers array. 3293 | * @param {Number} indexA 3294 | * @param {Number} indexB 3295 | * @example 3296 | * contros.swap(0, 3); // swap the first controller with the fourth 3297 | */ 3298 | swap(indexA: number, indexB: number): void; 3299 | /** 3300 | * Removes a controller from this controllers array 3301 | * by setting `contros[index] = null`. 3302 | * 3303 | * Newly connected controllers fill the first empty slot. 3304 | * @param {Number} index 3305 | */ 3306 | remove(index: number): void; 3307 | /** 3308 | * Runs when a controller is connected. By default it 3309 | * always returns true. Overwrite this function to customize 3310 | * the behavior. 3311 | * 3312 | * For example, it could be customized to filter 3313 | * controllers based on their model info. 3314 | * 3315 | * Doesn't run if a controller in the `controllers` array 3316 | * is reconnected. 3317 | * @type {Function} 3318 | * @param {Gamepad} gamepad 3319 | * @returns {Boolean} true if the controller should be added to this p5play controllers array 3320 | */ 3321 | onConnect(gamepad: Gamepad): boolean; 3322 | /** 3323 | * Runs when a controller is disconnected. by default it 3324 | * always returns false. Overwrite this function to customize 3325 | * the behavior. 3326 | * 3327 | * Removing a controller from the `controllers` array 3328 | * usually is not desirable, because the controller could be 3329 | * reconnected later. By default, the controller is kept in 3330 | * the array and its state is reset. 3331 | * @type {Function} 3332 | * @param {Gamepad} gamepad 3333 | * @returns {Boolean} true if the controllers should be removed from this p5play controllers array 3334 | */ 3335 | onDisconnect(gamepad: Gamepad): boolean; 3336 | } 3337 | var contros: _Contros; 3338 | var controllers: _Contros; 3339 | var contro: Contro; 3340 | 3341 | } 3342 | -------------------------------------------------------------------------------- /p5play.min.js: -------------------------------------------------------------------------------- 1 | if("undefined"==typeof p5)throw"You need to load q5.js or p5.js before p5play";if("object"!=typeof planck){if("object"!=typeof process)throw"You need to load planck.js before p5play!";global.planck=require("./planck.min.js")}let p5playInit=function(){const t=this,e=planck;let i=p5.disableFriendlyErrors;if(p5.disableFriendlyErrors=!0,"object"!=typeof process&&0!=window._p5play_gtagged){let t=document.createElement("script");t.src="https://www.googletagmanager.com/gtag/js?id=G-EHXNCTSYLK",t.async=!0,document.head.append(t),window._p5play_gtagged=!0,t.onload=()=>{window.dataLayer??=[],window.gtag=function(){dataLayer.push(arguments)},gtag("js",new Date),gtag("config","G-EHXNCTSYLK"),gtag("event","p5play_v3_29")}}let s=!t._q5&&1==p5.VERSION[0],r=!t._q5&&2==p5.VERSION[0];const o=t.DEGREES;t.angleMode(o);const h=(i,s,r)=>new e.Vec2(i*r/t.world.meterSize,s*r/t.world.meterSize),a=(i,s,r)=>new e.Vec2(i/r*t.world.meterSize,s/r*t.world.meterSize),n=e.Settings.linearSlop,l=e.Settings.angularSlop/60,d=t=>Math.abs(t)<=n,p=(t,e)=>Math.abs(t-Math.round(t))<=(e||n)?Math.round(t):t,u=(e,i)=>{let s=t._angleMode==o?360:t.TWO_PI,r=(e-i)%s,h=(s-Math.abs(r))*-Math.sign(r);return(Math.abs(r)-1){let e=navigator.userAgent.substring(t+10,t+12);this.os.platform="iOS",this.os.version=e}else{let t=navigator.userAgentData?.platform;!t&&navigator.platform&&(t=navigator.platform.slice(3),"Mac"==t?t="macOS":"Win"==t?t="Windows":"Lin"==t&&(t="Linux")),this.os.platform=t}}this.renderStats=!1,this._renderStats={x:10,y:20,font:"monospace"},this._fps=60,this._fpsArr=[60],this._collides={},this._colliding={},this._collided={},this._overlaps={},this._overlapping={},this._overlapped={}}onImageLoad(){}},this.p5play=new t.P5Play,delete t.P5Play;let _=!0,g=1,f=t.log=console.log;this.DYN="dynamic",this.DYNAMIC="dynamic",this.STA="static",this.STATIC="static",this.KIN="kinematic",this.KINEMATIC="kinematic",this.Sprite=class{constructor(i,s,r,o,h){this._isSprite=!0,this.idNum;let a,n,l=[...arguments];if(void 0!==l[0]&&l[0]._isGroup&&(a=l[0],l=l.slice(1)),void 0!==l[0]&&("string"==typeof l[0]||l[0]instanceof t.Ani||l[0]instanceof p5.Image)&&(n=l[0],l=l.slice(1)),1==l.length&&"number"==typeof l[0])throw new M("Sprite",0,[l[0]]);if(Array.isArray(l[0])){if(i=void 0,s=void 0,r=l[0],o=void 0,h=l[1],Array.isArray(h))throw new M("Sprite",1,[`[[${r}], [${o}]]`])}else i=l[0],s=l[1],r=l[2],o=l[3],h=l[4];"string"==typeof r&&(h=r,r=void 0),"string"==typeof o&&(!function(t){if("d"==t||"s"==t||"k"==t||"n"==t)return!0;let e=t.slice(0,2);return"dy"==e||"st"==e||"ki"==e||"no"==e}(o)?r=w(r,o):h=o,o=void 0),this.idNum=t.p5play.spritesCreated,this._uid=1e3+this.idNum,t.p5play.sprites[this._uid]=this,t.p5play.spritesCreated++,this.groups=[],this.animations=new t.Anis,this.joints=[],this.joints.removeAll=()=>{for(;this.joints.length;)this.joints.at(-1).remove()},this.watch,this.mod={},this._removed=!1,this._life=2147483647,this._visible=!0,this._pixelPerfect=!1,this._aniChangeCount=0,this._draw=()=>this.__draw(),this._hasOverlap={},this._collisions={},this._overlappers={},a??=t.allSprites,this._tile="",this.tileSize=a.tileSize||1;let d=this;this._position={x:0,y:0},this._pos=t.createVector.call(t),Object.defineProperty(this._pos,"x",{get(){if(!d.body||!_)return d._position.x;let e=d.body.getPosition().x/d.tileSize*t.world.meterSize;return t.p5play.friendlyRounding?p(e):e},set(i){if(d._position.x=i,d.body){let s=new e.Vec2(i*d.tileSize/t.world.meterSize,d.body.getPosition().y);d.body.setPosition(s)}}}),Object.defineProperty(this._pos,"y",{get(){if(!d.body||!_)return d._position.y;let e=d.body.getPosition().y/d.tileSize*t.world.meterSize;return t.p5play.friendlyRounding?p(e):e},set(i){if(d._position.y=i,d.body){let s=new e.Vec2(d.body.getPosition().x,i*d.tileSize/t.world.meterSize);d.body.setPosition(s)}}}),this._canvasPos=t.createVector.call(t),Object.defineProperty(this._canvasPos,"x",{get(){let e=d._pos.x-t.camera.x;return t._c2d&&(e+=t.canvas.hw/t.camera._zoom),e}}),Object.defineProperty(this._canvasPos,"y",{get(){let e=d._pos.y-t.camera.y;return t._c2d&&(e+=t.canvas.hh/t.camera._zoom),e}}),this._velocity={x:0,y:0},this._direction=0,this._vel=t.createVector.call(t),Object.defineProperties(this._vel,{x:{get(){let e;return e=d.body?d.body.getLinearVelocity().x:d._velocity.x,e/=d.tileSize,t.p5play.friendlyRounding?p(e):e},set(t){t*=d.tileSize,d.body?d.body.setLinearVelocity(new e.Vec2(t,d.body.getLinearVelocity().y)):d._velocity.x=t,(t||this.y)&&(d._direction=this.heading())}},y:{get(){let e;return e=d.body?d.body.getLinearVelocity().y:d._velocity.y,e/=d.tileSize,t.p5play.friendlyRounding?p(e):e},set(t){t*=d.tileSize,d.body?d.body.setLinearVelocity(new e.Vec2(d.body.getLinearVelocity().x,t)):d._velocity.y=t,(t||this.x)&&(d._direction=this.heading())}}}),this._mirror={_x:1,_y:1,get x(){return this._x<0},set x(t){d.watch&&(d.mod[20]=!0),this._x=t?-1:1},get y(){return this._y<0},set y(t){d.watch&&(d.mod[20]=!0),this._y=t?-1:1}},this._heading="right",this._layer=a._layer,this._layer??=t.allSprites._getTopLayer()+1,a.dynamic&&(h??="dynamic"),a.kinematic&&(h??="kinematic"),a.static&&(h??="static"),h??=a.collider,h&&"string"==typeof h||(h="dynamic"),this.collider=h,i??=a.x,void 0===i&&(i=t._c2d?t.canvas.hw/this.tileSize:0,r&&(this._vertexMode=!0)),s??=a.y,void 0===s&&(s=t._c2d?t.canvas.hh/this.tileSize:0);let u=!1;if(void 0===r&&(r=a.w||a.width||a.d||a.diameter||a.v||a.vertices,o||a.d||a.diameter||(o=a.h||a.height,u=!0)),"function"==typeof i&&(i=i(a.length)),"function"==typeof s&&(s=s(a.length)),"function"==typeof r&&(r=r(a.length)),"function"==typeof o&&(o=o(a.length)),this.x=i,this.y=s,!a._isAllSpritesGroup&&!n){for(let t in a.animations){n=t;break}n||(n=a._img,"function"==typeof n&&(n=n(a.length)),n&&(this._img=!0))}for(let e=a;e;e=t.p5play.groups[e.parent])this.groups.push(e);if(this.groups.reverse(),n){let e=this.tileSize;this._img||n instanceof p5.Image?(this.image="string"!=typeof n?n:new t.EmojiImage(n,r),r||1==this._img.w&&1==this._img.h||(r=(this._img.defaultWidth||this._img.w)/e,o??=(this._img.defaultHeight||this._img.h)/e)):("string"==typeof n?this._changeAni(n):this._ani=n.clone(),r||1==this._ani.w&&1==this._ani.h||(r=(this._ani.defaultWidth||this._ani.w)/e,o??=(this._ani.defaultHeight||this._ani.h)/e))}if(this.groups=[],this.mouse=new t._SpriteMouse,this._rotation=0,this._rotationSpeed=0,this._bearing=0,this._scale=new y,Object.defineProperty(this._scale,"x",{get(){return this._x},set(t){if(t==this._x)return;d.watch&&(d.mod[26]=!0);let e=Math.abs(t/this._x);d._w*=e,d._hw*=e,d._resizeColliders({x:e,y:1}),this._x=t,this._avg=.5*(this._x+this._y)}}),Object.defineProperty(this._scale,"y",{get(){return this._y},set(t){if(t==this._y)return;d.watch&&(d.mod[26]=!0);let e=Math.abs(t/this._y);d._h&&(this._h*=e,this._hh*=e),d._resizeColliders({x:1,y:e}),this._y=t,this._avg=.5*(this._x+this._y)}}),this._offset={_x:0,_y:0,get x(){return this._x},set x(t){t!=this._x&&(d.watch&&(d.mod[21]=!0),d._offsetCenterBy(t-this._x,0))},get y(){return this._y},set y(t){t!=this._y&&(d.watch&&(d.mod[21]=!0),d._offsetCenterBy(0,t-this._y))}},this._massUndef=!0,void 0===r&&(this._dimensionsUndef=!0,this._widthUndef=!0,r=this.tileSize>1?1:50,void 0===o&&(this._heightUndef=!0)),u&&(o??=this.tileSize>1?1:50),this._shape=a.shape,3!=this.__collider)this._vertexMode?this.addCollider(r):this.addCollider(0,0,r,o),this.shape=this._shape;else{if(this.w=r,Array.isArray(r))throw new Error('Cannot set the collider type of a sprite with a polygon or chain shape to "none". To achieve the same effect, use .overlaps(allSprites) to have your sprite overlap with the allSprites group.');void 0!==r&&void 0===o?this.shape="circle":(this.shape="box",this.h=o)}this.prevPos={x:i,y:s},this.prevRotation=0,this._dest={x:i,y:s},this._destIdx=0,this._debug=!1,this.text,a._isAllSpritesGroup||t.allSprites.push(this),a.push(this);let c=a.vel.x||0,g=a.vel.y||0;"function"==typeof c&&(c=c(a.length-1)),"function"==typeof g&&(g=g(a.length-1)),this.vel.x=c,this.vel.y=g;let f=["ani","collider","x","y","w","h","d","diameter","dynamic","height","kinematic","static","vel","width"];for(let e of t.Sprite.propsAll){if(f.includes(e))continue;let i=a[e];void 0!==i&&("function"==typeof i&&m(i)&&(i=i(a.length-1)),"object"==typeof i?i instanceof p5.Color?this[e]=t.color(i):this[e]=Object.assign({},i):this[e]=i)}f=["add","animation","animations","autoCull","contains","GroupSprite","Group","idNum","length","mod","mouse","p","parent","Sprite","Subgroup","subgroups","velocity"];for(let e=0;e1?1:50,o??=r,l=h(r-.08,o-.08,this.tileSize)),"box"==n)p=e.Box(l.x/2,l.y/2,h(i,s,this.tileSize),0);else if("circle"==n)p=e.Circle(h(i,s,this.tileSize),l.x/2);else if(a){let c,_,g=[{x:0,y:0}],f={x:0,y:0},y={x:0,y:0},m={x:0,y:0},v=Array.isArray(a[0]);function x(){f.xm.x&&(m.x=f.x),f.y>m.y&&(m.y=f.y)}if(v){this._vertexMode&&(c=a[0][0],_=a[0][1],this.fixture&&this._relativeOrigin?(c=this.x-this._relativeOrigin.x,_=this.y-this._relativeOrigin.y,g.pop()):(this.x=c,this.y=_));for(let S=0;S0?1:-1;A=Math.abs(A);let k=0;for(let M=0;Me.Settings.maxPolygonVertices||"chain"==this._shape)&&(n="chain"),"polygon"==n?p=e.Polygon(g):"chain"==n&&(p=e.Chain(g,!1))}if(this.shape??=n,this.fixtureList){this._extents??={t:this.hh,b:this.hh,l:this._hw,r:this._hw};let G=this._extents,N=i-.5*r,R=i+.5*r,E=s-.5*o,U=s+.5*o;NG.r&&(G.r=R),EG.b&&(G.b=U),this._totalWidth=G.r-G.l,this._totalHeight=G.b-G.t;let V=Math.abs;this._largestExtent=Math.max(V(G.l),V(G.r),V(G.t),V(G.b))}else this._w=r,this._hw=.5*r,1!=this.__shape&&(this._h=o,this._hh=.5*o);return p}removeColliders(){this.body&&(this._removeContacts(0),this._removeFixtures(0))}removeSensors(){this.body&&(this._removeContacts(1),this._removeFixtures(1),this._hasSensors=!1)}_removeFixtures(e){let i;for(let s=this.fixtureList;s;s=s.getNext())if(void 0===e||s.m_isSensor==e){let e=s.m_next;s.destroyProxies(t.world.m_broadPhase),i?i.m_next=e:this.body.m_fixtureList=e}else i=s}_removeContacts(e){if(!this.body)return;let i=this.body.m_contactList;for(;i;){let s=i.contact;i=i.next,void 0!==e&&s.m_fixtureA.m_isSensor!=e||t.world.destroyContact(s)}}_offsetCenterBy(t,e){if(!t&&!e)return;if(this._offset._x+=t,this._offset._y+=e,!this.body)return;let i=h(t,e,this.tileSize);this.__offsetCenterBy(i.x,i.y)}__offsetCenterBy(t,e){for(let i=this.body.m_fixtureList;i;i=i.m_next){let s=i.m_shape;if("circle"!=s.m_type){let i=s.m_vertices;for(let s of i)s.x+=t,s.y+=e}else s.m_p.x+=t,s.m_p.y+=e}}_cloneBodyProps(){let t={},e=["bounciness","density","drag","friction","heading","isSuperFast","rotation","rotationDrag","rotationLock","rotationSpeed","scale","vel","x","y"];this._massUndef&&this._dimensionsUndef||e.push("mass");for(let i of e)"object"==typeof this[i]?t[i]=Object.assign({},this[i]):t[i]=this[i];return t}get animation(){return this._ani}set animation(t){this.changeAni(t)}get ani(){return this._ani}set ani(t){this.changeAni(t)}get anis(){return this.animations}get autoUpdate(){return this._autoUpdate}set autoUpdate(t){this._autoUpdate=t}get autoDraw(){return this._autoDraw}set autoDraw(t){this._autoDraw=t}get allowSleeping(){return this.body?.isSleepingAllowed()}set allowSleeping(t){this.watch&&(this.mod[5]=!0),this.body&&this.body.setSleepingAllowed(t)}get bounciness(){if(this.fixture)return this.fixture.getRestitution()}set bounciness(t){this.watch&&(this.mod[7]=!0);for(let e=this.fixtureList;e;e=e.getNext())e.setRestitution(t)}get collider(){return this._collider}set collider(e){if(e==this._collider)return;let i=(e=e.toLowerCase())[0];if("d"==i&&(e="dynamic"),"s"==i&&(e="static"),"k"==i&&(e="kinematic"),"n"==i&&(e="none"),e==this._collider)return;if("none"==e&&("chain"==this._shape||"polygon"==this._shape))return void console.error('Cannot set the collider type of a polygon or chain collider to "none". To achieve the same effect, use .overlaps(allSprites) to have your sprite overlap with the allSprites group.');if(this._removed)throw new Error("Cannot change the collider type of a sprite that was removed.");let s=this.__collider;this._collider=e,this.__collider=["d","s","k","n"].indexOf(i),this.watch&&(this.mod[8]=!0),void 0!==s&&(3!=this.__collider?(this.body&&this.body.setType(e),3==s&&(this.addCollider(),this.x=this._position.x,this.y=this._position.y,this.vel.x=this._velocity.x,this.vel.y=this._velocity.y,this.rotation=this._rotation,this.rotationSpeed=this._rotationSpeed)):(this.removeColliders(),this.fixture?.m_isSensor?this.body.m_gravityScale=0:(this._syncWithPhysicsBody(),t.world.destroyBody(this.body),this.body=null)))}get physicsType(){return this._collider}set physicsType(t){this.collider=t}get physics(){return this._collider}set physics(t){this.collider=t}_syncWithPhysicsBody(){this._position.x=this.x,this._position.y=this.y,this._velocity.x=this.vel.x,this._velocity.y=this.vel.y,this._rotation=this.rotation,this._rotationSpeed=this.rotationSpeed}_parseColor(e){return e instanceof p5.Color?e:"object"!=typeof e&&1==e.length?t.colorPal(e):t.color(e)}get color(){return this._color}set color(t){this.watch&&(this.mod[9]=!0),this._color=this._parseColor(t)}get colour(){return this._color}set colour(t){this.color=t}get fill(){return this._color}set fill(t){this.color=t}get stroke(){return this._stroke}set stroke(t){this.watch&&(this.mod[29]=!0),this._stroke=this._parseColor(t)}get strokeWeight(){return this._strokeWeight}set strokeWeight(t){this.watch&&(this.mod[30]=!0),this._strokeWeight=t}get textColor(){return this._textFill}set textColor(t){this.watch&&(this.mod[32]=!0),this._textFill=this._parseColor(t)}get textColour(){return this._textFill}set textColour(t){this.textColor=t}get textFill(){return this._textFill}set textFill(t){this.textColor=t}get textSize(){return this._textSize}set textSize(t){this.watch&&(this.mod[33]=!0),this._textSize=t}get textStroke(){return this._textStroke}set textStroke(t){this.watch&&(this.mod[34]=!0),this._textStroke=this._parseColor(t)}get textStrokeWeight(){return this._textStrokeWeight}set textStrokeWeight(t){this.watch&&(this.mod[35]=!0),this._textStrokeWeight=t}get tile(){return this._tile}set tile(t){this.watch&&(this.mod[36]=!0),this._tile=t}get tileSize(){return this._tileSize}set tileSize(t){this.watch&&(this.mod[37]=!0),this._tileSize=t}get bearing(){return this._bearing}set bearing(t){this.watch&&(this.mod[6]=!0),this._bearing=t}get debug(){return this._debug}set debug(t){this.watch&&(this.mod[10]=!0),this._debug=t}get density(){if(this.fixture)return this.fixture.getDensity()}set density(t){this.watch&&(this.mod[11]=!0);for(let e=this.fixtureList;e;e=e.getNext())e.setDensity(t)}_getDirectionAngle(e){e=e.toLowerCase().replaceAll(/[ _-]/g,"");let i={up:-90,down:90,left:180,right:0,upright:-45,rightup:-45,upleft:-135,leftup:-135,downright:45,rightdown:45,downleft:135,leftdown:135,forward:this.rotation,backward:this.rotation+180}[e];return"radians"==t._angleMode&&(i=t.radians(i)),i}get direction(){return 0!==this.vel.x||0!==this.vel.y?t.atan2(this.vel.y,this.vel.x):this._isTurtleSprite?this.rotation:this._direction}set direction(e){this.watch&&(this.mod[12]=!0),"string"==typeof e&&(this._heading=e,e=this._getDirectionAngle(e)),this._direction=e,this._isTurtleSprite&&(this.rotation=e);let i=this.speed;this.vel.x=t.cos(e)*i,this.vel.y=t.sin(e)*i}get drag(){return this.body?.getLinearDamping()}set drag(t){this.watch&&(this.mod[13]=!0),this.body&&this.body.setLinearDamping(t)}get draw(){return this._display}set draw(t){this._userDefinedDraw=!0,this._draw=t}get dynamic(){return this.body?.isDynamic()}set dynamic(t){this.collider=t?"dynamic":"kinematic"}get fixture(){return this.fixtureList}get fixtureList(){return this.body?this.body.m_fixtureList:null}get friction(){if(this.fixture)return this.fixture.getFriction()}set friction(t){this.watch&&(this.mod[14]=!0);for(let e=this.fixtureList;e;e=e.getNext())e.setFriction(t)}get heading(){return this._heading}set heading(t){this.direction=t}get img(){return this._img||this._ani?.frameImage}set img(t){this.image=t}get image(){return this._img||this._ani?.frameImage}set image(e){"string"==typeof e&&(e=e.includes(".")?t.loadImage(e):new t.EmojiImage(e,this.w)),this._img=this._extendImage(e)}_extendImage(t){return t.offset??={x:0,y:0},t._scale??={x:1,y:1},t.scale||Object.defineProperty(t,"scale",{get:()=>t._scale,set:e=>{"number"==typeof e&&(e={x:e,y:e}),t._scale=e}}),t}get isMoving(){return 0!=this.vel.x||0!=this.vel.y}get isSuperFast(){return this.body?.isBullet()}set isSuperFast(t){this.watch&&(this.mod[16]=!0),this.body&&this.body.setBullet(t)}get kinematic(){return this.body?.isKinematic()}set kinematic(t){this.collider=t?"kinematic":"dynamic"}get layer(){return this._layer}set layer(t){this.watch&&(this.mod[17]=!0),this._layer=t}get life(){return this._life}set life(t){this.watch&&(this.mod[18]=!0),this._life=t}get mass(){return this.body?.getMass()}set mass(t){if(!this.body)return;this.watch&&(this.mod[19]=!0);const i={I:0,center:new e.Vec2(this.body.getLocalCenter()),mass:0};this.body.getMassData(i),i.mass=t>0?t:1e-8,this.body.setMassData(i),delete this._massUndef}resetMass(){if(!this.body)return;let t=new e.Vec2(this.body.getLocalCenter());this.watch&&(this.mod[19]=!0),this.body.resetMassData(),this.body.setMassData({mass:this.body.getMass(),center:t,I:this.body.getInertia()})}resetCenterOfMass(){this.watch&&(this.mod[19]=!0),this.body.resetMassData();let{x:t,y:e}=this.body.getLocalCenter();if(0==t&&0==e)return;this.__offsetCenterBy(-t,-e),this.body.resetMassData();let i=this.body.getPosition();this.body.setPosition({x:i.x+t,y:i.y+e})}get mirror(){return this._mirror}set mirror(t){this.watch&&(this.mod[20]=!0),void 0!==t.x&&(this._mirror.x=t.x),void 0!==t.y&&(this._mirror.y=t.y)}get offset(){return this._offset}set offset(t){t.x??=this._offset._x,t.y??=this._offset._y,t.x==this._offset._x&&t.y==this._offset._y||(this.watch&&(this.mod[21]=!0),this._offsetCenterBy(t.x-this._offset._x,t.y-this._offset._y))}get opacity(){return this._opacity??1}set opacity(t){this.watch&&(this.mod[41]=!0),this._opacity=t}get previousPosition(){return this.prevPos}set previousPosition(t){this.prevPos=t}get previousRotation(){return this.prevRotation}set previousRotation(t){this.prevRotation=t}get pixelPerfect(){return this._pixelPerfect}set pixelPerfect(t){this.watch&&(this.mod[22]=!0),this._pixelPerfect=t}get removed(){return this._removed}set removed(t){t&&!this._removed&&(this.watch&&(this.mod[23]=!0),this._removed=!0,this._remove())}get rotation(){if(!this.body||!_)return this._rotation||0;let e=this.body.getAngle();return t.p5play.friendlyRounding&&(e=p(e,l)),t._angleMode==o?t.degrees(e):e}set rotation(e){this._rotation=e,this.body&&(t._angleMode==o&&(e=t.radians(e%360)),this.body.setAngle(e),this.body.synchronizeTransform())}get rotationDrag(){return this.body?.getAngularDamping()}set rotationDrag(t){this.body&&(this.watch&&(this.mod[24]=!0),this.body.setAngularDamping(t))}get rotationLock(){return this.body?.isFixedRotation()}set rotationLock(t){if(!this.body)return;this.watch&&(this.mod[25]=!0);let e=this.mass;this.body.setFixedRotation(t),this.mass=e}get rotationSpeed(){if(this.body){let e=this.body.getAngularVelocity()/60;return t._angleMode==o?t.degrees(e):e}return this._rotationSpeed}set rotationSpeed(e){this.body?(e*=60,t._angleMode==o&&(e=t.radians(e)),this.body.setAngularVelocity(e)):this._rotationSpeed=e}get scale(){return this._scale}set scale(t){if(0==t&&(t=.01),"number"==typeof t?t={x:t,y:t}:(t.x??=this._scale._x,t.y??=this._scale._y),t.x==this._scale._x&&t.y==this._scale._y)return;this.watch&&(this.mod[26]=!0);let e={x:Math.abs(t.x/this._scale._x),y:Math.abs(t.y/this._scale._y)};this._w*=e.x,this._hw*=e.x,this._h&&(this._h*=e.y,this._hh*=e.y),this._resizeColliders(e),this._scale._x=t.x,this._scale._y=t.y,this._scale._avg=t.x}get sleeping(){if(this.body)return!this.body.isAwake()}set sleeping(t){this.body&&(this.watch&&(this.mod[28]=!0),this.body.setAwake(!t))}get speed(){return t.createVector(this.vel.x,this.vel.y).mag()}set speed(e){let i=this.direction;this.vel.x=t.cos(i)*e,this.vel.y=t.sin(i)*e}get static(){return this.body?.isStatic()}set static(t){this.collider=t?"static":"dynamic"}get tint(){return this._tint}set tint(t){this.watch&&(this.mod[38]=!0),this._tint=this._parseColor(t)}get tintColor(){return this._tint}set tintColor(t){this.tint=t}set vertices(t){if(3==this.__collider)throw new Error('Cannot set vertices of a sprite with collider type of "none".');this.watch&&(this.mod[27]=!0),this._removeFixtures(),this._originMode="start",this.addCollider(t),this._hasSensors&&this.addDefaultSensors()}get vertices(){return this._getVertices()}_getVertices(e){let i=this.fixture.getShape(),s=[...i.m_vertices];"polygon"==i.m_type&&s.unshift(s.at(-1));let r=this.x,o=this.y;for(let i=0;i=2)return void console.error('Cannot set the collider shape to chain or polygon if the sprite has a collider type of "none". To achieve the same effect, use .overlaps(allSprites) to have your sprite overlap with the allSprites group.');let s,r,o=this.__shape;if(this.__shape=i,this._shape=e,this.watch&&(this.mod[27]=!0),void 0===o)return;if(0==this.__shape?(this._h=this._w,this._hh=this._hw):(this._h=void 0,this._hh=void 0),1!=o&&1!=this.__shape?s=this._getVertices(!0):r=this._w,this._removeFixtures(),3!=this.__collider)if(s)this._originMode??="center",this.addCollider(s);else if(1==o){let t=this._w*Math.sin(Math.PI/12);this.addCollider(0,0,[t,-30,12])}else this.addCollider();this._hasSensors&&this.addDefaultSensors();let h=this._offset._x,a=this._offset._y;(h||a)&&(this._offset._x=0,this._offset._y=0,this._offsetCenterBy(h,a))}get update(){return this._update}set update(t){this._customUpdate=t}get postDraw(){return this._postDraw}set postDraw(t){this._customPostDraw=t}get vel(){return this._vel}set vel(t){this.vel.x=t.x,this.vel.y=t.y}get velocity(){return this._vel}set velocity(t){this.vel=t}get gravityScale(){return this.body?.getGravityScale()}set gravityScale(t){this.body&&(this.watch&&(this.mod[42]=!0),this.body.setGravityScale(t))}_update(){this._customUpdate&&this._customUpdate(),this.autoUpdate&&(this.autoUpdate=null)}_step(){this.life-=g,2147483647!=this._life&&this._life<=0&&this.remove(),this.body&&_||this._removed||(this._position.x+=this.vel.x*g,this._position.y+=this.vel.y*g,this._rotation+=this._rotationSpeed*g),this.watch&&(this.x!=this.prevX&&(this.mod[0]=this.mod[2]=!0),this.y!=this.prevY&&(this.mod[1]=this.mod[2]=!0),this.rotation!=this.prevRotation&&(this.mod[3]=this.mod[4]=!0)),(this.body||this._removed)&&this.__step()}__step(){let e,i=this;for(let s in c)for(let r in this[s]){if(r>=1e3){if(i._isGroup||i._uid>=r)continue;e=t.p5play.sprites[r]}else{if(i._isGroup&&i._uid>=r)continue;e=t.p5play.groups[r]}let o=i[s][r]+1;e&&0!=o&&-2!=o?(this[s][r]=o,e[s][i._uid]=o):(delete i[s][r],e&&delete e[s][i._uid])}}___step(){let e,i,s,r,o=this,h=!0;for(let a in c){for(let n in this[a]){if(n>=1e3){if(o._isGroup||o._uid>=n)continue;e=t.p5play.sprites[n]}else{if(o._isGroup&&o._uid>=n)continue;e=t.p5play.groups[n]}if(o._isGroup||e?._isGroup)continue;if(s=o._hasOverlap[e._uid]??e._hasOverlap[o._uid],h&&!1!==s||!h&&!0!==s)continue;let l=o[a][n];for(let s=0;s<3;s++){if(0==s&&1!=l&&-3!=l)continue;if(1==s&&-1==l)continue;if(2==s&&l>=1)continue;i=c[a][s];let h=t.p5play[i][o._uid];if(h){r=h[e._uid],r&&r.call(o,o,e,l);for(let t of e.groups)r=h[t._uid],r&&r.call(o,o,e,l)}let n=t.p5play[i][e._uid];if(n){r=n[o._uid],r&&r.call(e,e,o,l);for(let t of o.groups)r=n[t._uid],!r||h&&r==h[t._uid]||r.call(e,e,o,l)}}}h=!1}if(this._removed&&0==Object.keys(this._collisions).length&&0==Object.keys(this._overlappers).length){this._isSprite?delete t.p5play.sprites[this._uid]:t.p5play.storeRemovedGroupRefs||delete t.p5play.groups[this._uid];for(let e in c)for(let i of c[e])delete t.p5play[i][this._uid]}}__draw(){if(!t.p5play.disableImages)if(this._ani)this._ani.draw(this._offset._x,this._offset._y,0,this._scale._x,this._scale._y);else if(this._img){let e=this._img,i=1!=this._scale._x||1!=this._scale._y||1!=e.scale.x||1!=e.scale.y;i&&(t.push(),t.scale(this._scale._x*e.scale.x,this._scale._y*e.scale.y)),t.image(e,this._offset._x+e.offset.x,this._offset._y+e.offset.y),i&&t.pop()}if(!this._ani&&!this._img||this.debug||t.p5play.disableImages)if(this.debug&&(t.noFill(),t.stroke(0,255,0),t.line(0,-2,0,2),t.line(-2,0,2,0)),3!=this.__collider){this.debug||0===this._strokeWeight?t.noStroke():2==this.__shape?t.stroke(this.stroke||this.color):this._stroke&&t.stroke(this._stroke);for(let e=this.fixtureList;e;e=e.getNext()){if(this.debug)e.m_isSensor?t.stroke(255,255,0,127):t.stroke(0,255,0,127);else if(e.m_isSensor)continue;this._drawFixture(e)}}else 0!==this._strokeWeight&&t.stroke(this._stroke||120),0==this.__shape?t.rect(this._offset._x,this._offset._y,this.w*this.tileSize,this.h*this.tileSize):1==this.__shape&&t.circle(this._offset._x,this._offset._y,this.d*this.tileSize);void 0!==this.text&&(t.textAlign(t.CENTER,t.CENTER),t.fill(this._textFill),this._textStrokeWeight&&t.strokeWeight(this._textStrokeWeight),this._textStroke?t.stroke(this._textStroke):t.noStroke(),t.textSize(this.textSize*this.tileSize),t.text(this.text,0,0))}_postDraw(){this._ani?.update&&this._ani.update();for(let t in this.mouse)-1==this.mouse[t]&&(this.mouse[t]=0);this._customPostDraw&&this._customPostDraw(),this.autoDraw??=!0,this.autoUpdate??=!0}_display(){let e,i=this.x*this.tileSize+t.world.origin.x,s=this.y*this.tileSize+t.world.origin.y;if(!this._userDefinedDraw){let e;if(e=this._totalWidth?Math.max(this._totalWidth,this._totalHeight):void 0!==this._h?Math.max(this._w,this._h):this._w,this.ani&&!t.p5play.disableImages&&(e=Math.max(e,this.ani.w,this.ani.h)),"chain"!=this.shape&&t.camera.isActive&&(i+et.camera.bound.max.x||s+et.camera.bound.max.y))return void(this._visible=null)}if(this._visible=!0,t.p5play.spritesDrawn++,this._pixelPerfect){let e,r;this.ani&&this.ani.length&&!t.p5play.disableImages?(e=this.ani[this.ani._frame].w,r=this.ani[this.ani._frame].h):(e=this._w,r=this._h),i=e%2==0?Math.round(i):Math.round(i-.5)+.5,s=r%2==0?Math.round(s):Math.round(s-.5)+.5}else i=p(i),s=p(s);for(let t of this.joints)t.visible?this._uid==t.spriteA._uid?(!t.spriteB._visible||this.layer<=t.spriteB.layer)&&t._display():(!t.spriteA._visible||this.layer{let s,r;do{if(await t.sleep(),c!=this._destIdx)return!1;let o=this.direction<0?this.direction+360:this.direction;if(u&&(o<=n||o>=l)||Math.abs(this.vel.x)<=d&&Math.abs(this.vel.y)<=d)return!1;e&&(s=this.vel.x>0?this._dest.x-this.x:this.x-this._dest.x),i&&(r=this.vel.y>0?this._dest.y-this.y:this.y-this._dest.y)}while(e&&s>p||i&&r>p);return this.x=this._dest.x,this.y=this._dest.y,this.vel.x=0,this.vel.y=0,!0})()}rotateTowards(t,e){if(1==this.__collider)return void new M(0);let i,s,r,o=arguments;"number"!=typeof o[0]?(i=o[0].x,s=o[0].y,e=o[1],r=o[2]):arguments.length>2&&(i=o[0],s=o[1],e=o[2],r=o[3]),void 0!==i?t=this.angleToFace(i,s,r):t-=this.rotation,e??=.1,this.rotationSpeed=t*e}angleTo(e,i){if("object"==typeof e){let s=e;if(s==t.mouse&&!t.mouse.isActive)return 0;if(void 0===s.x||void 0===s.y)return console.error("sprite.angleTo ERROR: rotation destination not defined, object given with no x or y properties"),0;i=s.y,e=s.x}return t.atan2(i-this.y,e-this.x)}rotationToFace(t,e,i){return"object"==typeof t&&(i=e,e=t.y,t=t.x),Math.abs(t-this.x)<.01&&Math.abs(e-this.y)<.01?0:this.angleTo(t,e)+(i||0)}angleToFace(t,e,i){let s=this.rotationToFace(t,e,i);return u(s,this.rotation)}rotateTo(e,i,s){if(1==this.__collider)return void new M(0);let r=arguments;if("number"!=typeof r[0]?e=this.rotationToFace(r[0].x,r[0].y,s):r.length>2&&(s=r[3],i=r[2],e=this.rotationToFace(r[0],r[1],s)),e==this.rotation)return;let h=t._angleMode==o?360:t.TWO_PI;return(e=(e-this.rotation)%h)<0&&i>0&&(e+=h),e>0&&i<0&&(e-=h),i??=this.rotationSpeed||Math.sign(e),this.rotate(e,i)}rotateMinTo(t,e,i){if(1==this.__collider)return void new M(0);let s=arguments;return"number"!=typeof s[0]?t=this.rotationToFace(s[0].x,s[0].y,i):s.length>2&&(i=s[3],e=s[2],t=this.rotationToFace(s[0],s[1],i)),t!=this.rotation?(t=u(t,this.rotation),e??=this.rotationSpeed>.1?this.rotationSpeed:1,e=Math.abs(e)*Math.sign(t),this.rotate(t,e)):void 0}rotate(e,i){if(1==this.__collider)return void new M(0);if(isNaN(e))return void new M(1,[e]);if(0==e)return;i??=this.rotationSpeed||1;let s=e>0&&i>0;s||(e=-Math.abs(e),i=-Math.abs(i)),this.rotationSpeed=i;let r=Math.abs(i),o=this.rotation+e;this._rotateIdx??=0,this._rotateIdx++;let h=this._rotateIdx;return(async()=>{let e=.01;do{let a=Math.abs(o-this.rotation);if(r>a&&(this.rotationSpeed=a*Math.sign(i)),await t.sleep(),this._rotateIdx!=h)return!1;if(s&&this.rotationSpeed-.01)return!1}while((s&&o>this.rotation||!s&&o1)e=[...arguments];else if(e instanceof t.Ani){if(e==this._ani)return;e=[e]}else if(!Array.isArray(e)){if(e==this._ani?.name)return;e=[e]}let i,s;this._aniChangeCount++;for(let r=0;r1&&("!"==o.name[0]&&(o.name=o.name.slice(1),o.start=-1,o.end=0),"<"!=o.name[0]&&">"!=o.name[0]||(o.name=o.name.slice(1),o.flipX=!0),"^"==o.name[0]&&(o.name=o.name.slice(1),o.flipY=!0),"**"==o.name&&(i=!0,e=e.slice(0,-1)),";;"==o.name&&(s=!0,e=e.slice(0,-1)))}let r=this._aniChangeCount;do{for(let t=0;t1&&(i.start=0),await this._playSequencedAni(i)}}while(i&&r==this._aniChangeCount);1!=e.length&&s&&this._ani.stop()}_playSequencedAni(t){return new Promise((e=>{let{name:i,start:s,end:r,flipX:o,flipY:h}=t;this._changeAni(i),o&&(this._ani.scale.x=-this._ani.scale.x),h&&(this._ani.scale.y=-this._ani.scale.y),s<0&&(s=this._ani.length+s),void 0!==s&&(this._ani._frame=s),void 0!==r?this._ani.goToFrame(r):this._ani._frame==this._ani.lastFrame&&e(),this._ani._onComplete=this._ani._onChange=()=>{o&&(this._ani.scale.x=-this._ani.scale.x),h&&(this._ani.scale.y=-this._ani.scale.y),this._ani._onComplete=this._ani._onChange=null,e()}}))}changeAnimation(){return this.changeAni(...arguments)}_changeAni(e){this._ani?._onChange&&this._ani._onChange(),this._ani?.onChange&&this._ani.onChange();let i=this.animations[e];if(!i)for(let t=this.groups.length-1;t>=0;t--){if(i=this.groups[t].animations[e],i){i=i.clone();break}}if(!i)throw t.noLoop(),new M("Sprite.changeAnimation",[e]);this._ani=i,this._ani.name=e,this.resetAnimationsOnChange&&(this._ani._frame=0)}remove(){this.removed=!0}_remove(){this.body&&t.world.destroyBody(this.body),this.body=null;for(let t of this.groups)t.remove(this)}toString(){return"s"+this.idNum}_setContactCB(e,i,s,r){let o;o=0==s?c._collisions[r]:c._overlappers[r];let h=t.p5play[o],a=h[this._uid]??={};a[e._uid]!=i&&(a[e._uid]=i,a=h[e._uid],a&&a[this._uid]&&(delete a[this._uid],0==Object.keys(a).length&&delete h[e._uid]))}_validateCollideParams(t,e){if(!t)throw new M("Sprite.collide",2);if(!t._isSprite&&!t._isGroup)throw new M("Sprite.collide",0,[t]);if(e&&"function"!=typeof e)throw new M("Sprite.collide",1,[e])}_ensureCollide(t,e,i){if(!1!==this._hasOverlap[t._uid]&&(this._hasOverlap[t._uid]=!1),!1!==t._hasOverlap[this._uid]&&(t._hasOverlap[this._uid]=!1,t._isGroup))for(let e of t)e._hasOverlap[this._uid]=!1,this._hasOverlap[e._uid]=!1}collide(t,e){return this.collides(t,e)}collides(t,e){return this._validateCollideParams(t,e),this._ensureCollide(t),e&&this._setContactCB(t,e,0,0),1==this._collisions[t._uid]||this._collisions[t._uid]<=-3}colliding(t,e){this._validateCollideParams(t,e),this._ensureCollide(t),e&&this._setContactCB(t,e,0,1);let i=this._collisions[t._uid];return i<=-3?1:i>0?i:0}collided(t,e){return this._validateCollideParams(t,e),this._ensureCollide(t),e&&this._setContactCB(t,e,0,2),this._collisions[t._uid]<=-1}_validateOverlapParams(t,e){if(!t)throw new M("Sprite.overlap",2);if(!t._isSprite&&!t._isGroup)throw new M("Sprite.overlap",0,[t]);if(e&&"function"!=typeof e)throw new M("Sprite.overlap",1,[e])}_ensureOverlap(t){if(this._hasSensors||this.addDefaultSensors(),!t._hasSensors)if(t._isSprite)t.addDefaultSensors();else{for(let e of t)e._hasSensors||e.addDefaultSensors();t._hasSensors=!0}if(this._hasOverlap[t._uid]||(this._removeContactsWith(t),this._hasOverlap[t._uid]=!0),!t._hasOverlap[this._uid]&&(t._removeContactsWith(this),t._hasOverlap[this._uid]=!0,t._isGroup))for(let e of t)e._hasOverlap[this._uid]=!0,this._hasOverlap[e._uid]=!0}overlap(t,e){return this.overlaps(t,e)}overlaps(t,e){return this._validateOverlapParams(t,e),this._ensureOverlap(t),e&&this._setContactCB(t,e,1,0),1==this._overlappers[t._uid]||this._overlappers[t._uid]<=-3}overlapping(t,e){this._validateOverlapParams(t,e),this._ensureOverlap(t),e&&this._setContactCB(t,e,1,1);let i=this._overlappers[t._uid];return i<=-3?1:i>0?i:0}overlapped(t,e){return this._validateOverlapParams(t,e),this._ensureOverlap(t),e&&this._setContactCB(t,e,1,2),this._overlappers[t._uid]<=-1}_removeContactsWith(t){if(t._isGroup)for(let e of t)this._removeContactsWith(e);else this.__removeContactsWith(t)}__removeContactsWith(e){if(this.body)for(let i=this.body.getContactList();i;i=i.next){let s=i.contact;s.m_fixtureA.m_body.sprite._uid!=e._uid&&s.m_fixtureB.m_body.sprite._uid!=e._uid||t.world.destroyContact(s)}}_sortFixtures(){let t,e,i=null,s=null;for(let r=this.fixtureList;r;r=r.getNext())r.m_isSensor?(s?s.m_next=r:s=r,e=r):(i?i.m_next=r:i=r,t=r);s&&(e.m_next=null),i&&(t.m_next=s),this.body.m_fixtureList=i||s}addDefaultSensors(){let t;if(this.body&&this.fixtureList){for(let e=this.fixtureList;e;e=e.getNext())e.m_isSensor||(t=e.m_shape,this.body.createFixture({shape:t,isSensor:!0}));this._sortFixtures()}else this.addSensor();this._hasSensors=!0}distanceTo(e){return t.dist(this.x,this.y,e.x,e.y)}},t.Sprite.propTypes={x:"Float64",y:"Float64",vel:"Vec2",rotation:"number",rotationSpeed:"number",allowSleeping:"boolean",bearing:"number",bounciness:"number",collider:"Uint8",color:"color",debug:"boolean",density:"number",direction:"number",drag:"number",friction:"number",h:"number",isSuperFast:"boolean",layer:"number",life:"Int32",mass:"number",mirror:"Vec2_boolean",offset:"Vec2",pixelPerfect:"boolean",removed:"boolean",rotationDrag:"number",rotationLock:"boolean",scale:"Vec2",shape:"Uint8",sleeping:"boolean",stroke:"color",strokeWeight:"number",text:"string",textColor:"color",textSize:"number",textStroke:"color",textStrokeWeight:"number",tile:"string",tileSize:"number",tint:"color",visible:"boolean",w:"number",opacity:"number",gravityScale:"number"},t.Sprite.props=Object.keys(t.Sprite.propTypes),t.Sprite.propsAll=t.Sprite.props.concat(["autoDraw","autoUpdate","colour","d","diameter","dynamic","fill","height","heading","kinematic","resetAnimationsOnChange","speed","spriteSheet","static","textColour","textFill","width"]),t.Sprite.colliderTypes=["d","s","k","n"],t.Sprite.shapeTypes=["box","circle","chain","polygon"],t.Turtle=function(e){if(t.allSprites.tileSize>1)throw new Error("Turtle can't be used when allSprites.tileSize is greater than 1.");e??=25;let i=new t.Sprite(e,e,[[e,.4*e],[-e,.4*e],[0,.8*-e]]);i.color="green",i._isTurtleSprite=!0,i._prevPos={x:i.x,y:i.y};let s=i.move;return i.move=function(){return this._prevPos.x=this.x,this._prevPos.y=this.y,s.call(this,...arguments)},i},this.Ani=class extends Array{constructor(){super();let e,i=[...arguments];if(this.name="default","object"==typeof i[0]&&(i[0]._isSprite||i[0]._isGroup)&&(e=i[0],i=i.slice(1),this._addedToSpriteOrGroup=!0),e??=t.allSprites,"string"!=typeof i[0]||1!=i[0].length&&i[0].includes(".")||(this.name=i[0],i=i.slice(1)),this._frame=0,this._cycles=0,this.targetFrame=-1,this.offset={x:e.anis.offset.x??0,y:e.anis.offset.y??0},this._frameDelay=e.anis.frameDelay||4,this.demoMode=e.anis.demoMode??!1,this.playing=!0,this.visible=!0,this.looping=e.anis.looping??!0,this.endOnFirstFrame=e.anis.endOnFirstFrame??!1,this.frameChanged=!1,this.onComplete=this.onChange=null,this._onComplete=this._onChange=null,this.rotation=e.anis.rotation??0,this._scale=new y,0!=i.length&&"number"!=typeof i[0]){if(e.animations[this.name]=this,e._ani=this,Array.isArray(i[0])&&"string"==typeof i[0][0]&&(i=[...i[0]]),2!=i.length||"string"!=typeof i[0]||"string"!=typeof i[1]&&"number"!=typeof i[1])if("string"==typeof i.at(-1)||i.at(-1)instanceof p5.Image)for(let s=0;s=3)throw new M("Ani",1);o=i[0],r=i[1]}else r=i[0];let h=this;if(o instanceof p5.Image&&1!=o.width&&1!=o.height)this.spriteSheet=o,a();else{let n;n="string"==typeof o?o:o.url,t._incrementPreload(),this.spriteSheet=t.loadImage(n,(()=>{a(),t._decrementPreload()})),"string"==typeof o&&(e.spriteSheet=this.spriteSheet)}function a(){Array.isArray(r)&&(r="object"==typeof r[0]?{frames:r}:4==r.length?{pos:r.slice(0,2),size:r.slice(2)}:{pos:r});let{w:i,h:s,width:o,height:a,size:n,row:l,col:d,line:p,x:u,y:c,pos:_,frames:g,frameCount:f,frameDelay:y,frameSize:m,delay:w,rotation:v}=r;m??=n||e.anis.frameSize,w&&(h.frameDelay=w),y&&(h.frameDelay=y),v&&(h.rotation=v),g&&Array.isArray(g)?f=g.length:f??=g||1,i??=o||e.anis.w,s??=a||e.anis.h,u??=d||0,c??=p||l||0,_&&(u=_[0],c=_[1]),"number"==typeof m?i=s=m:m&&(i=m[0],s=m[1]);let x=e.tileSize;if(i&&s?(i*=x,s*=x):!e._dimensionsUndef&&e.w&&e.h?(i??=e.w*x,s??=e.h*x):1!=x?(i??=x,s??=x):f?(i??=h.spriteSheet.width/f,s??=h.spriteSheet.height):i=s=h.spriteSheet.width=h.spriteSheet.width&&(u=0,c+=s,c>=h.spriteSheet.height&&(c=0))}}}}else{let l,d,p=i[0];isNaN(i[1])?l=i[1]:d=Number(i[1]);let u=p.lastIndexOf("."),c=0,_=0;for(let w=u-1;w>=0&&!isNaN(p.charAt(w));w--)c++;if(l)for(let v=l.length-5;v>=0&&!isNaN(l.charAt(v));v--)_++;let g,f=p.slice(u),m=p.slice(0,u-c);if(l&&(g=l.slice(0,u-_)),l&&m!=g)this.push(t.loadImage(p)),this.push(t.loadImage(l));else{let x,b=parseInt(p.slice(u-c,u),10);if(d??=parseInt(l.slice(u-_,u),10),d=this.length)throw new M("Ani.frame",[t,this.length]);this._frame=t,this._cycles=0}get frameDelay(){return this._frameDelay}set frameDelay(t){t<=0&&(t=1),this._frameDelay=t}get scale(){return this._scale}set scale(t){"number"==typeof t&&(t={x:t,y:t}),this._scale._x=t.x,this._scale._y=t.y,this._scale._avg=t.x}clone(){this.length||console.error(`The animation named "${this.name}" must be loaded before it can be properly copied. Sprites need their own copy of a group's animation. Try loading the animation in the preload function and creating new group sprites in the setup function.`);let e=new t.Ani;e.spriteSheet=this.spriteSheet;for(let t=0;tthis._frame&&-1!==this.targetFrame?this._frame++:this.targetFrame=this.lastFrame?this._frame=0:this._frame++:this._frame{this._onComplete=()=>{this._onComplete=null,t()}}))}pause(t){this.playing=!1,t&&(this._frame=t)}stop(t){this.playing=!1,t&&(this._frame=t)}rewind(){return this.looping=!1,this.goToFrame(0)}loop(){this.looping=!0,this.playing=!0}noLoop(){this.looping=!1}nextFrame(){this._frame0?this._frame=this._frame-1:this.looping&&(this._frame=this.length-1),this.targetFrame=-1,this.playing=!1,this._cycles=0}goToFrame(t){if(!(t<0||t>=this.length))return this.targetFrame=t,this._cycles=0,this.targetFrame!==this._frame&&(this.playing=!0),new Promise((t=>{this._onComplete=()=>{this._onComplete=null,t()}}))}get lastFrame(){return this.length-1}get frameImage(){let e=this[this._frame];if(e instanceof p5.Image)return e;let{x:i,y:s,w:r,h:o}=e,h=t.createImage(r,o);return h.copy(this.spriteSheet,this.offset.x,this.offset.y,r,o,i,s,r,o),h}get w(){return this.width}get width(){let t=this[this._frame];return t instanceof p5.Image?t.width:t?t.w:1}get defaultWidth(){return this[this._frame].defaultWidth}get h(){return this.height}get height(){let t=this[this._frame];return t instanceof p5.Image?t.height:t?t.h:1}get defaultHeight(){return this[this._frame].defaultHeight}},t.Ani.props=["demoMode","endOnFirstFrame","frameDelay","frameSize","looping","offset","rotation","scale"],this.Anis=class{#t={};constructor(){let e=this,i=[...t.Ani.props,"w","h"],s=["offset","scale"];for(let t of i)Object.defineProperty(this,t,{get:()=>e.#t[t],set(i){e.#t[t]=i;for(let s in e){let r=e[s];r instanceof Ani&&(r[t]=i)}}});for(let t of s){this.#t[t]={_x:0,_y:0};for(let i of["x","y"])Object.defineProperty(this.#t[t],i,{get:()=>e.#t[t]["_"+i],set(s){e.#t[t]["_"+i]=s;for(let r in e){let o=e[r];o instanceof Ani&&(o[t][i]=s)}}})}}get width(){return this.w}set width(t){this.w=t}get height(){return this.h}set height(t){this.h=t}},this.Group=class extends Array{constructor(...e){let i;if(e[0]instanceof t.Group&&(i=e[0],e=e.slice(1)),super(...e),"number"==typeof e[0])return;for(let e of this)if(!(e instanceof t.Sprite))throw new Error("A group can only contain sprites");if(this._isGroup=!0,this.x,this.y,this.vel,this.rotation,this.rotationSpeed,this.autoDraw,this.allowSleeping,this.autoUpdate,this.bounciness,this.collider,this.color,this.debug,this.density,this.direction,this.drag,this.friction,this.h,this.isSuperFast,this.layer,this.life,this.mass,this.mirror,this.offset,this.pixelPerfect,this.removed,this.rotationDrag,this.rotationLock,this.scale,this.shape,this.sleeping,this.stroke,this.strokeWeight,this.text,this.textColor,this.tile,this.tileSize,this.visible,this.w,this.bearing,this.d,this.dynamic,this.heading,this.kinematic,this.resetAnimationsOnChange,this.speed,this.static,this.idNum,t.p5play.groupsCreated<999)this.idNum=t.p5play.groupsCreated;else{for(let e=1;et&&(t=e._layer);return t}get ani(){return this._ani}set ani(t){this.addAni(t);for(let e of this)e.changeAni(t)}get animation(){return this._ani}set animation(t){this.ani=t}get anis(){return this.animations}get img(){return this._img}set img(t){this.image=t}get image(){return this._img}set image(e){"function"!=typeof e?("string"==typeof e&&(e=e.includes(".")?t.loadImage(e):new t.EmojiImage(e,this.w||this.width||this.d||this.diameter)),this._img=t.Sprite.prototype._extendImage(e)):this._img=e}get amount(){return this.length}set amount(t){let e=t-this.length,i=e>0;e=Math.abs(e);for(let t=0;t0?i:0}collided(t,e){return this._validateCollideParams(t,e),this._ensureCollide(t),e&&this._setContactCB(t,e,0,2),this._collisions[t._uid]<=-1}_validateOverlapParams(t,e){if(e&&"function"!=typeof e)throw new M("Group.overlap",1,[e]);if(!t)throw new M("Group.overlap",2);if(!t._isGroup&&!t._isSprite)throw new M("Group.overlap",0,[t])}_ensureOverlap(t){if(!this._hasSensors){for(let t of this)t._hasSensors||t.addDefaultSensors();this._hasSensors=!0}if(!t._hasSensors)if(t._isSprite)t.addDefaultSensors();else{for(let e of t)e._hasSensors||e.addDefaultSensors();t._hasSensors=!0}if(1!=this._hasOverlap[t._uid]){this._removeContactsWith(t),this._hasOverlap[t._uid]=!0;for(let e of this)if(e._hasOverlap[t._uid]=!0,t._hasOverlap[e._uid]=!0,this._uid==t._uid)for(let i of t)e._hasOverlap[i._uid]=!0,i._hasOverlap[e._uid]=!0}if(1!=t._hasOverlap[this._uid]&&(t._removeContactsWith(this),t._hasOverlap[this._uid]=!0,t._isGroup))for(let e of t){e._hasOverlap[this._uid]=!0,this._hasOverlap[e._uid]=!0;for(let t of this)e._hasOverlap[t._uid]=!0,t._hasOverlap[e._uid]=!0}}overlap(t,e){return this.overlaps(t,e)}overlaps(t,e){return this._validateOverlapParams(t,e),this._ensureOverlap(t),e&&this._setContactCB(t,e,1,0),1==this._overlappers[t._uid]||this._overlappers[t._uid]<=-3}overlapping(t,e){this._validateOverlapParams(t,e),this._ensureOverlap(t),e&&this._setContactCB(t,e,1,1);let i=this._overlappers[t._uid];return i<=-3?1:i>0?i:0}overlapped(t,e){return this._validateOverlapParams(t,e),this._ensureOverlap(t),e&&this._setContactCB(t,e,1,2),this._overlappers[t._uid]<=-1}_removeContactsWith(t){for(let e of this)e._removeContactsWith(t)}applyForce(){for(let t of this)t.applyForce(...arguments)}applyForceScaled(){for(let t of this)t.applyForceScaled(...arguments)}attractTo(){for(let t of this)t.attractTo(...arguments)}applyTorque(){for(let t of this)t.applyTorque(...arguments)}move(t,e,i){let s=[];for(let r of this)s.push(r.move(t,e,i));return Promise.all(s)}moveTo(e,i,s){if("number"!=typeof e){let r=e;if(r==t.mouse&&!t.mouse.isActive)return;s=i,i=r.y,e=r.x}let r=this._resetCentroid(),o=[];for(let t of this){let h={x:t.x-r.x+e,y:t.y-r.y+i};o.push(t.moveTo(h.x,h.y,s))}return Promise.all(o)}moveTowards(e,i,s){if("number"!=typeof e){let r=e;if(r==t.mouse&&!t.mouse.isActive)return;s=i,i=r.y,e=r.x}if(void 0!==e||void 0!==i){this._resetCentroid();for(let t of this){void 0===t.distCentroid&&this._resetDistancesFromCentroid();let r={x:t.distCentroid.x+e,y:t.distCentroid.y+i};t.moveTowards(r.x,r.y,s)}}}moveAway(e,i,s){if("number"!=typeof e){let r=e;if(r==t.mouse&&!t.mouse.isActive)return;s=i,i=r.y,e=r.x}if(void 0!==e||void 0!==i){this._resetCentroid();for(let t of this){void 0===t.distCentroid&&this._resetDistancesFromCentroid();let r={x:t.distCentroid.x+e,y:t.distCentroid.y+i};t.moveAway(r.x,r.y,s)}}}push(...e){this.removed&&(console.warn("Adding a sprite to a group that was removed. Use `group.removeAll()` to remove all of a group's sprites without removing the group itself. Restoring the group in p5play's memory."),t.p5play.groups[this._uid]=this,this.removed=!1);for(let i of e){if(!(i instanceof t.Sprite))throw new Error("You can only add sprites to a group, not "+typeof i);if(i.removed){console.error("Can't add a removed sprite to a group");continue}let e;for(let s in this._hasOverlap){let r=this._hasOverlap[s];r&&!i._hasSensors&&i.addDefaultSensors(),e=s>=1e3?t.p5play.sprites[s]:t.p5play.groups[s],e&&!e.removed&&(r?e._ensureOverlap(i):e._ensureCollide(i))}for(let e in c){let s=c[e];for(let e of s){let s=t.p5play[e],r=s[this._uid];if(!r)continue;let o=s[i._uid]??={};for(let t in r)o[t]=r[t]}}super.push(i),this.parent&&t.p5play.groups[this.parent].push(i),i.groups.push(this)}return this.length}repelFrom(){for(let t of this)t.repelFrom(...arguments)}size(){return this.length}toString(){return"g"+this.idNum}cull(e,i,s,r,o){if(void 0===s){o=i,e=i=s=r=e}if(isNaN(e)||isNaN(i)||isNaN(s)||isNaN(r))throw new TypeError("The culling boundary must be defined with numbers");if(o&&"function"!=typeof o)throw new TypeError("The callback to group.cull must be a function");let h=t.camera.x-t.canvas.hw/t.camera.zoom,a=t.camera.y-t.canvas.hh/t.camera.zoom,n=-s+h,l=-e+a,d=t.width+r+h,p=t.height+i+a,u=0;for(let t=0;td||e.y>p)&&(u++,o?o(e,u):e.remove(),e.removed&&t--))}return u}remove(t){if(void 0===t)return this.removeAll(),void(this._isAllSpritesGroup||(this.removed=!0));let e;if(e="number"==typeof t?t>=0?t:this.length+t:this.indexOf(t),-1==e)return;let i=this[e];return this.splice(e,1),i}splice(e,i){let s=super.splice(e,i);if(!s)return;let r=[];for(let t of s){if(t.removed)continue;let e=this._uid;do{r.push(e);let i=t.groups.findIndex((t=>t._uid==e)),s=t.groups.splice(i,1);e=s[0].parent}while(e)}for(let e of r){let i=t.p5play.groups[e];for(let e in c)for(let s in i[e]){if(0==i[e][s])continue;let r;r=s>=1e3?t.p5play.sprites[s]:t.p5play.groups[s];let o=!1;for(let t of i)if(t[e][r._uid]>0){o=!0;break}o||(i[e][r._uid]=-2,r[e][i._uid]=-2)}}return s}pop(){return this.remove(this.length-1)}shift(){return this.remove(0)}unshift(){return console.error("unshift is not supported for groups"),this.length}removeAll(){for(;this.length>0;)this.at(-1).remove()}_step(){this.__step()}update(){for(let e of this)t.p5play._inPostDraw&&!e.autoUpdate||e.update();this._autoUpdate&&(this._autoUpdate=null)}draw(){let e=[...this];e.sort(((t,e)=>t._layer-e._layer));for(let i of e)!1===i._visible||t.p5play._inPostDraw&&!i.autoDraw||i.draw();this._autoDraw&&(this._autoDraw=null)}postDraw(){for(let t of this)t.postDraw()}},t.Group.prototype.addAni=t.Group.prototype.addAnimation=t.Sprite.prototype.addAnimation=t.Sprite.prototype.addAni,t.Group.prototype.addAnis=t.Group.prototype.addAnimations=t.Sprite.prototype.addAnimations=t.Sprite.prototype.addAnis,t.Group.prototype.__step=t.Sprite.prototype.__step,t.Group.prototype.___step=t.Sprite.prototype.___step,this.World=class extends planck.World{constructor(){super(new e.Vec2(0,0),!0),this.mod={},this.origin={x:0,y:0},this.contacts=[],this.on("begin-contact",this._beginContact),this.on("end-contact",this._endContact);let i=this;this._gravity={get x(){return i.m_gravity.x},set x(e){if((e=Math.round(e||0))!=i.m_gravity.x){i.mod[0]=!0;for(let e of t.allSprites)e.sleeping=!1;i.m_gravity.x=e}},get y(){return i.m_gravity.y},set y(e){if((e=Math.round(e||0))!=i.m_gravity.y){i.mod[0]=!0;for(let e of t.allSprites)e.sleeping=!1;i.m_gravity.y=e}}},this._timeScale=1,this._updateRate=60,this._syncedToFrameRate=!0,this._lastStepTime=0,this._setTimeStep(),this.velocityIterations=8,this.positionIterations=3,this.velocityThreshold=.19,this.physicsTime=0,this.meterSize=60,this.mouseTracking??=!0,this.mouseSprite=null,this.mouseSprites=[],this.autoStep=!0,this.step=this.physicsUpdate,window.Event&&(this.steppedEvent=new window.Event("p5play_worldStepped"))}get gravity(){return this._gravity}set gravity(t){this._gravity.x=t.x,this._gravity.y=t.y}get timeScale(){return this._timeScale}set timeScale(t){if(t<0||t>2)return console.error("world.timeScale must be between 0 and 2");this._timeScale!=t&&(this._timeScale=t,this._setTimeStep())}get updateRate(){return this._updateRate}set updateRate(e){this._updateRate=e,this._syncedToFrameRate=e==t._targetFrameRate,this._setTimeStep()}_setTimeStep(){this._timeStep=1/this._updateRate*this._timeScale}get velocityThreshold(){return e.Settings.velocityThreshold}set velocityThreshold(t){e.Settings.velocityThreshold=t}physicsUpdate(e,i,s){_=!0,g=this._timeScale;for(let e of t.allSprites)e.prevPos.x=e.x,e.prevPos.y=e.y,e.prevRotation=e.rotation;e??=this._timeStep,super.step(e,i||this.velocityIterations,s||this.positionIterations),this.physicsTime+=e;let r=Object.values(t.p5play.sprites),o=Object.values(t.p5play.groups);for(let t of r)t._step();for(let t of o)t._step();for(let t of r)t.___step();for(let t of o)t.___step();if(t.canvas.dispatchEvent&&t.canvas.dispatchEvent(this.steppedEvent),!this._syncedToFrameRate)for(let e of t.allSprites)e._syncWithPhysicsBody();this.autoStep&&(this.autoStep=null)}extrapolationUpdate(e){e??=this._timeStep;for(let e of t.allSprites)e.prevPos.x=e.x,e.prevPos.y=e.y,e.prevRotation=e.rotation;_=!1,g=e/this._timeStep*this._timeScale;let i=Object.values(t.p5play.sprites),s=Object.values(t.p5play.groups);for(let t of i)t._step();for(let t of s)t._step();this.autoStep&&(this.autoStep=null)}get realTime(){return t.millis()/1e3}getSpritesAt(i,s,r,o=!0){"object"==typeof i&&(o=r??!0,r=s,s=i.y,i=i.x);const h=new e.Vec2(i/this.meterSize,s/this.meterSize),a=new e.AABB;a.lowerBound=new e.Vec2(h.x-.001,h.y-.001),a.upperBound=new e.Vec2(h.x+.001,h.y+.001);let n=[];if(this.queryAABB(a,(t=>(t.getShape().testPoint(t.getBody().getTransform(),h)&&n.push(t),!0))),0==n.length)return[];r??=t.allSprites;let l=[];for(let t of n){const e=t.m_body.sprite;e._cameraActiveWhenDrawn==o&&(l.find((t=>t._uid==e._uid))||l.push(e))}return l.sort(((t,e)=>-1*(t._layer-e._layer))),l}getSpriteAt(t,e,i){return this.getSpritesAt(t,e,i)[0]}getMouseSprites(){let e=this.getSpritesAt(t.mouse.x,t.mouse.y);if(t.camera._wasOff){let i=this.getSpritesAt(t.mouse.canvasPos.x,t.mouse.canvasPos.y,t.allSprites,!1);i.length&&(e=[...i,...e])}return e}_beginContact(t){let e=t.m_fixtureA,i=t.m_fixtureB,s="_collisions";e.m_isSensor&&(s="_overlappers"),e=e.m_body.sprite,i=i.m_body.sprite,e[s][i._uid]=0,i[s][e._uid]=0;for(let t of i.groups)(!e[s][t._uid]||e[s][t._uid]<0)&&(e[s][t._uid]=0,t[s][e._uid]=0);for(let t of e.groups){(!i[s][t._uid]||i[s][t._uid]<0)&&(i[s][t._uid]=0,t[s][i._uid]=0);for(let e of i.groups)(!t[s][e._uid]||t[s][e._uid]<0)&&(t[s][e._uid]=0,e[s][t._uid]=0)}}_endContact(t){let e=t.m_fixtureA,i=t.m_fixtureB,s="_collisions";e.m_isSensor&&(s="_overlappers"),e=e.m_body.sprite,i=i.m_body.sprite,e[s][i._uid]=0!=e[s][i._uid]?-2:-4,i[s][e._uid]=0!=i[s][e._uid]?-2:-4;for(let t of i.groups){let i=!1;for(let r of t)if(r[s][e._uid]>=0){i=!0;break}i||(t[s][e._uid]=0!=t[s][e._uid]?-2:-4,e[s][t._uid]=0!=e[s][t._uid]?-2:-4)}for(let t of e.groups){let e=!1;for(let r of t)if(r[s][i._uid]>=0){e=!0;break}if(!e){t[s][i._uid]=0!=t[s][i._uid]?-2:-4,i[s][t._uid]=0!=i[s][t._uid]?-2:-4;for(let e of i.groups)t[s][e._uid]=0!=t[s][e._uid]?-2:-4,e[s][t._uid]=0!=e[s][t._uid]?-2:-4}}}_findContact(t,e,i){let s=e[t][i._uid];if(s)return s;for(let r of i.groups)if(s=e[t][r._uid],s)return s;for(let r of e.groups){if(s=r[t][i._uid],s)return s;for(let e of i.groups)if(r._uid==e._uid&&(s=r[t][e._uid],s))return s}return!1}get allowSleeping(){return this.getAllowSleeping()}set allowSleeping(t){this.setAllowSleeping(t)}rayCast(t,e,i){return this.rayCastAll(t,e,i,(()=>!0))[0]}rayCastAll(e,i,s,r){let o,a=t.allSprites.tileSize,n=h(e.x,e.y,a);if("number"==typeof arguments[1])o=h(e.x+s*t.cos(i),e.y+s*t.sin(i),a);else{let t=arguments[1];r??=arguments[2],o=h(t.x,t.y,a)}let l=[],d=1;super.rayCast(n,o,(function(t,e,i,s){let o=t.getBody().sprite,h=r&&r(o);return l.push({sprite:o,fraction:s}),h?(st.fraction-e.fraction));let p=[];for(let t of l)t.fraction<=d&&p.push(t.sprite);return p}},this.Camera=class{constructor(){this._pos=t.createVector.call(t),this.__pos={x:0,y:0,rounded:{}},this.isActive=!1,this.bound={min:{x:0,y:0},max:{x:0,y:0}},this._zoomIdx=-1,this._zoom=1,this._destIdx=0}get pos(){return this._pos}set pos(t){this.x=t.x,this.y=t.y}get position(){return this._pos}set position(t){this.x=t.x,this.y=t.y}_calcBoundsX(e){let i=t.canvas.hw/this._zoom;this.bound.min.x=e-i,this.bound.max.x=e+i}_calcBoundsY(e){let i=t.canvas.hh/this._zoom;this.bound.min.y=e-i,this.bound.max.y=e+i}get x(){return this._pos.x}set x(e){if(void 0===e||isNaN(e))return;this._pos.x=e;let i=-e;t._c2d&&(i+=t.canvas.hw/this._zoom),this.__pos.x=i,t.allSprites.pixelPerfect&&(this.__pos.rounded.x=Math.round(i)),this._calcBoundsX(e)}get y(){return this._pos.y}set y(e){if(void 0===e||isNaN(e))return;this._pos.y=e;let i=-e;t._c2d&&(i+=t.canvas.hh/this._zoom),this.__pos.y=i,t.allSprites.pixelPerfect&&(this.__pos.rounded.y=Math.round(i)),this._calcBoundsY(e)}moveTo(e,i,s){if(void 0===e)return;if(isNaN(e)&&(s=i,i=e.y,e=e.x),s??=1,s<=0)return console.warn("camera.moveTo: speed should be a positive number"),Promise.resolve(!1);let r=i-this.y,o=e-this.x,h=Math.sqrt(r*r+o*o),a=s/h,n=o*a,l=r*a;this._destIdx++;let d=this._destIdx,p=Math.ceil(h/s);return(async()=>{for(let e=0;e{for(let e=0;e0&&(e=e<.1?t.map(e,0,.1,30,4):e<.5?t.map(e,.1,.5,4,2.5):e<.8?t.map(e,.5,.8,2.5,1):e<.9?t.map(e,.8,.9,1,.5):t.map(e,.9,1,.5,.2)),this._springiness=e,"wheel"!=this.type)return this._j.setFrequency(e);this._j.setSpringFrequencyHz(e)}get damping(){return"wheel"!=this.type?this._j.getDampingRatio():this._j.getSpringDampingRatio()}set damping(t){"wheel"==this.type?this._j.setSpringDampingRatio(t):this._j.setDampingRatio(t)}get speed(){return this._j.getJointSpeed()}set speed(t){this._j.isMotorEnabled()||this._j.enableMotor(!0),this._j.setMotorSpeed(t)}get motorSpeed(){return this._j.getMotorSpeed()}get enableMotor(){return this._j.isMotorEnabled()}set enableMotor(t){this._j.enableMotor(t)}get maxPower(){return this._j.getMaxMotorTorque()}set maxPower(t){!this._j.isMotorEnabled()&&t&&this._j.enableMotor(!0),this._j.setMaxMotorTorque(t),t||this._j.enableMotor(!1)}get power(){return this._j.getMotorTorque()}get collideConnected(){return this._j.getCollideConnected()}set collideConnected(t){this._j.m_collideConnected=t}get reactionForce(){return this._j.getReactionForce(t.world._timeStep)}get reactionTorque(){return this._j.getReactionTorque(t.world._timeStep)}remove(){this._removed||(this.spriteA.joints.splice(this.spriteA.joints.indexOf(this),1),this.spriteB.joints.splice(this.spriteB.joints.indexOf(this),1),t.world.destroyJoint(this._j),this._removed=!0)}},this.GlueJoint=class extends t.Joint{constructor(t,e){super(...arguments,"glue")}},this.DistanceJoint=class extends t.Joint{constructor(t,i){super(...arguments,"distance");let s=e.DistanceJoint({},t.body,i.body,t.body.getWorldCenter(),i.body.getWorldCenter());this._createJoint(s)}_display(){let t,e;(this.offsetA.x||this.offsetA.y)&&(t=this.spriteA.body.getWorldPoint(this._j.m_localAnchorA),t=a(t.x,t.y,this.spriteA.tileSize)),(this.offsetB.x||this.offsetB.y)&&(e=this.spriteB.body.getWorldPoint(this._j.m_localAnchorB),e=a(e.x,e.y,this.spriteB.tileSize)),this._draw(t?t.x:this.spriteA.x,t?t.y:this.spriteA.y,e?e.x:this.spriteB.x,e?e.y:this.spriteB.y),this.visible=null}},this.WheelJoint=class extends t.Joint{constructor(i,s){super(...arguments,"wheel");let r=e.WheelJoint({maxMotorTorque:1e3,frequencyHz:4,dampingRatio:.7},i.body,s.body,s.body.getWorldCenter(),new e.Vec2(0,1));this._createJoint(r),this._angle=t._angleMode==o?90:1.5707963267948966}_display(){let e,i,s=this.spriteA.x,r=this.spriteA.y;if(this.offsetB.x||this.offsetB.y){let t=this.spriteB.body.getWorldPoint(this._j.m_localAnchorB);t=a(t.x,t.y,this.spriteB.tileSize),e=t.x,i=t.y}else e=this.spriteB.x,i=this.spriteB.y;let o=t.tan(this.spriteA.rotation),h=t.tan(this._angle+this.spriteA.rotation),n=(i-r+o*s-h*e)/(o-h),l=o*(n-s)+r;this._draw(n,l,e,i),this.visible=null}get angle(){return this._angle}set angle(i){i!=this._angle&&(this._angle=i,this._j.m_localXAxisA=new e.Vec2(t.cos(i),t.sin(i)),this._j.m_localXAxisA.normalize(),this._j.m_localYAxisA=e.Vec2.crossNumVec2(1,this._j.m_localXAxisA))}},this.HingeJoint=class extends t.Joint{constructor(t,i){super(...arguments,"hinge");let s=e.RevoluteJoint({},t.body,i.body,t.body.getWorldCenter());this._createJoint(s)}_display(){const e=this.offsetA.x,i=this.offsetA.y,s=this.spriteA.rotation,r=e*t.cos(s)-i*t.sin(s),o=e*t.sin(s)+i*t.cos(s);this._draw(this.spriteA.x+r,this.spriteA.y+o),this.visible=null}get range(){return this.upperLimit-this.lowerLimit}set range(t){t/=2,this.upperLimit=t,this.lowerLimit=-t}get lowerLimit(){let e=this._j.getLowerLimit();return"radians"==t._angleMode?e:t.degrees(e)}set lowerLimit(e){this._j.isLimitEnabled()||this._j.enableLimit(!0),this.spriteA.body.setAwake(!0),this.spriteB.body.setAwake(!0),t._angleMode==o&&(e=t.radians(e)),this._j.m_lowerAngle=e}get upperLimit(){let e=this._j.getUpperLimit();return"radians"==t._angleMode?e:t.degrees(e)}set upperLimit(e){this._j.isLimitEnabled()||this._j.enableLimit(!0),this.spriteA.body.setAwake(!0),this.spriteB.body.setAwake(!0),t._angleMode==o&&(e=t.radians(e)),this._j.m_upperAngle=e}get angle(){let e=this._j.getJointAngle();return"radians"==t._angleMode?e:t.radians(e)}},t.RevoluteJoint=t.HingeJoint,this.SliderJoint=class extends t.Joint{constructor(t,i){super(...arguments,"slider");let s=e.PrismaticJoint({lowerTranslation:-1,upperTranslation:1,enableLimit:!0,maxMotorForce:50,motorSpeed:0,enableMotor:!0},t.body,i.body,t.body.getWorldCenter(),new e.Vec2(1,0));this._createJoint(s),this._angle=0}get angle(){return this._angle}set angle(i){i!=this._angle&&(this._angle=i,this._j.m_localXAxisA=new e.Vec2(t.cos(i),t.sin(i)),this._j.m_localXAxisA.normalize(),this._j.m_localYAxisA=e.Vec2.crossNumVec2(1,this._j.m_localXAxisA))}get range(){return this.upperLimit-this.lowerLimit}set range(t){t/=2,this.upperLimit=t,this.lowerLimit=-t}get lowerLimit(){return this._j.getLowerLimit()/this.spriteA.tileSize*t.world.meterSize}set lowerLimit(e){this._j.isLimitEnabled()||this._j.enableLimit(!0),e=e*this.spriteA.tileSize/t.world.meterSize,this._j.setLimits(e,this._j.getUpperLimit())}get upperLimit(){return this._j.getUpperLimit()/this.spriteA.tileSize*t.world.meterSize}set upperLimit(e){this._j.isLimitEnabled()||this._j.enableLimit(!0),e=e*this.spriteA.tileSize/t.world.meterSize,this._j.setLimits(this._j.getLowerLimit(),e)}},t.PrismaticJoint=t.SliderJoint,this.RopeJoint=class extends t.Joint{constructor(t,i){super(...arguments,"rope");let s=e.RopeJoint({maxLength:1},t.body,i.body,t.body.getWorldCenter());this._createJoint(s),this._j.m_localAnchorB.x=0,this._j.m_localAnchorB.y=0}get maxLength(){return e=this._j.getMaxLength(),i=this.spriteA.tileSize,e/i*t.world.meterSize;var e,i}set maxLength(e){var i,s;this._j.setMaxLength((i=e,s=this.spriteA.tileSize,i*s/t.world.meterSize))}},this.GrabberJoint=class extends this.Joint{constructor(t){super(t,t,"grab"),this._target={x:0,y:0},this.__target=new e.Vec2(0,0);let i=e.MouseJoint({maxForce:1e3,frequencyHz:3,dampingRatio:.9,target:t.body.getPosition()},t.body,t.body);this._createJoint(i)}_draw(){t.line(this.spriteA.x,this.spriteA.y,this._target.x,this._target.y)}get target(){return this._target}set target(e){this._target.x=e.x,this._target.y=e.y,this.__target.x=e.x/t.world.meterSize,this.__target.y=e.y/t.world.meterSize,this._j.setTarget(this.__target)}get maxForce(){return this._j.getMaxForce()}set maxForce(t){this._j.setMaxForce(t)}};class y{constructor(){let t=this;Object.defineProperties(this,{x:{get:()=>t._x,set(e){e!=t._x&&(t._x=e,t._avg=.5*(t._x+t._y))},configurable:!0,enumerable:!0},y:{get:()=>t._y,set(e){e!=t._y&&(t._y=e,t._avg=.5*(t._x+t._y))},configurable:!0,enumerable:!0},_x:{value:1,enumerable:!1,writable:!0},_y:{value:1,enumerable:!1,writable:!0},_avg:{value:1,enumerable:!1,writable:!0}})}valueOf(){return this._avg}}function m(t){return!/^(?:(?:\/\*[^(?:\*\/)]*\*\/\s*)|(?:\/\/[^\r\n]*))*\s*(?:(?:(?:async\s(?:(?:\/\*[^(?:\*\/)]*\*\/\s*)|(?:\/\/[^\r\n]*))*\s*)?function|class)(?:\s|(?:(?:\/\*[^(?:\*\/)]*\*\/\s*)|(?:\/\/[^\r\n]*))*)|(?:[_$\w][\w0-9_$]*\s*(?:\/\*[^(?:\*\/)]*\*\/\s*)*\s*\()|(?:\[\s*(?:\/\*[^(?:\*\/)]*\*\/\s*)*\s*(?:(?:['][^']+['])|(?:["][^"]+["]))\s*(?:\/\*[^(?:\*\/)]*\*\/\s*)*\s*\]\())/.test(t.toString())}function w(t,e){let i=t,s=e.toLowerCase();if("triangle"==s?i=[i,-120,3]:"square"==s?i=[i,-90,4]:"pentagon"==s?i=[i,-72,5]:"hexagon"==s?i=[i,-60,6]:"septagon"==s?i=[i,-51.4285714286,7]:"octagon"==s?i=[i,-45,8]:"enneagon"==s?i=[i,-40,9]:"decagon"==s?i=[i,-36,10]:"hendecagon"==s?i=[i,-32.7272727273,11]:"dodecagon"==s&&(i=[i,-30,12]),i==t)throw new Error("Invalid, not a regular polygon: "+e);return i}if(t.p5play.palettes=[{a:"aqua",b:"black",c:"crimson",d:"darkviolet",e:"peachpuff",f:"olive",g:"green",h:"hotpink",i:"indigo",j:"navy",k:"khaki",l:"lime",m:"magenta",n:"brown",o:"orange",p:"pink",q:"turquoise",r:"red",s:"skyblue",t:"tan",u:"blue",v:"violet",w:"white",x:"gold",y:"yellow",z:"gray"}],this.colorPal=(e,i)=>{if(e instanceof p5.Color)return e;"number"==typeof i&&(i=t.p5play.palettes[i]),i??=t.p5play.palettes[0];let s=i[e];return s?t.color(s):t.color(0,0,0,0)},this.EmojiImage=function(e,i){let s=1.25*(i*=t.p5play.emojiScale),r=t.createGraphics(s,s,t.P2D);r.textSize(i),r.textAlign(t.CENTER),r.textFont(t.canvas.webgpu?t._g.textFont():t.textFont()),r.text(e,s/2,i);let o=r.drawingContext,h=r._pixelDensity||1,a=r.canvas.width,n=r.canvas.height,l=o.getImageData(0,0,a,n).data,d=a,p=0,u=n,c=0,_=3;for(let t=0;tp&&(p=e),tc&&(c=t)),_+=4;return u=Math.floor(u/h),c=Math.floor(c/h),d=Math.floor(d/h),p=Math.floor(p/h),r=r.get(d,u,p-d+1,c-u+1),r.url=e,r},this.spriteArt=(e,i,s)=>{i??=1,"number"==typeof s&&(s=t.p5play.palettes[s]),s??=t.p5play.palettes[0];let r=e;"string"==typeof e&&(r=(e=(e=(e=e.trim()).replace(/\r*\n\t+/g,"\n")).replace(/\s+$/g,"")).split("\n"));let o=0;for(let t of r)t.length>o&&(o=t.length);let h=r.length,a=t.createImage(o*i,h*i);a.loadPixels();for(let t=0;tnew Promise(t?e=>{setTimeout(e,t)}:requestAnimationFrame),this.sleep=e=>e?t.delay(e):new Promise((e=>{if(t.canvas.dispatchEvent){t.canvas.addEventListener("p5play_worldStepped",(function i(){t.canvas.removeEventListener("p5play_worldStepped",i),e()}))}else setTimeout(e,1e3*t.world._timeStep)})),this.play=t=>{if(!t?.play)throw new Error("Tried to play your sound but it wasn't a sound object.");return new Promise((e=>{t.play(),t.onended((()=>e()))}))},window.location){let e=location.hostname;switch(e){case"":case"127.0.0.1":case"localhost":case"p5play.org":case"editor.p5js.org":case"codepen.io":case"codera.app":case"aug4th.com":case"cdpn.io":case"glitch.com":case"replit.com":case"stackblitz.com":case"jsfiddle.net":case"aijs.io":case"preview-aijs.web.app":case"quinton-ashley.github.io":break;default:if(/^[\d\.]+$/.test(e)||e.endsWith(".lan")||e.endsWith("stackblitz.io")||e.endsWith("glitch.me")||e.endsWith("replit.dev")||e.endsWith("codehs.com")||e.endsWith("openprocessing.org")||location.origin.endsWith("preview.p5js.org"))break;!async function(){if(document.getElementById("p5play-intro"))return;r||t._incrementPreload();let e=document.createElement("div");e.id="p5play-intro",e.style="position: absolute; width: 100%; height: 100%; top: 0; left: 0; z-index: 1000; background-color: black;";let i=document.createElement("img");i.style="position: absolute; top: 50%; left: 50%; width: 80vmin; height: 40vmin; margin-left: -40vmin; margin-top: -20vmin; z-index: 1001; opacity: 1; scale: 1; transition: scale 1.5s, opacity 0.4s ease-in-out;",i.onerror=()=>{i.style.imageRendering="pixelated",i.src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAABACAYAAADS1n9/AAABbGlDQ1BQaG90b3Nob3AgSUNDIHByb2ZpbGUAACiRfZC/S0JhGIWfm4VgNkQNDg13igYr0SCXBjWQIkisQG26Xn+C2sfVCKMtWkPoP8igOWgoIgxaGhqCqCGitqamgpaSL+69hS51lvfhcDgcXujxaEKUeoFypWbEo2E1kUypzhcUFCxpelWEYrEFk39vlxT4uLezt+Nm12u7uRvciV6Wj88XNx88k/wvVyZb1YEvwKcLowaKF4ht1ITJW8CwkUimQGmYnLf5wOS0zSdWZjkeAeUaUPWClgHlFfCmu/x8F5dL6/rPMnO9O1tZWTJ7gBFmKVJFUEKjjkqMwB/5KSsfYQ1BHYMieQrUUAkhrIYsKnNU0JnAi4ofH3785p/tuo+7n/95O972M8y0pJRnHW++BUfT4DrteGNBGOyHq1OhGZplOYCeXA7eDmEgCUM34Fqt5gJ+e707DH1PUr6PgnMP2g0pP/elbDfB8QgXlW8Y/mq9pjC8gwAABSxJREFUeJztnb9y2zAMxulcpnbNntfJ3D5n97xO966Z3aNP9NHgBxAgQVmO8bvLNZZIiCI+gH8kpyl4bk7P3gErOJ/PZ0+zp9NpmZ9CAM54O7+wSgQhAEdWOb+wQgSvzZHAj5+/50x9/WkOeRMZwIkm+medXyAi8M4CL82R4KkIATjQRL8nJJN4XysEsAKv9L8DIYAnJyaBk8CUTDLA51/bbP7jHWSQRZPByADeTDp/tM4ozT4AVHSwO1kEMBMY/dTLFNcMcN5oSgTHYHBi2fPrS4qo98Mh/a+C8/ErdyJYw8cbb/bzH/ncGQY8aOYAnpThZ4XGerZdrn209fxIe6rVQw52Oif4NquAfF+d+Y6NSeej9C9F/714WAHkyP5uoxcSCBKSic5WMhwCciTlcnUaRSmVRlw5x0UiVx6VqW1Ru1zbaHl6TXS9Efae3GmuNzpXYDMA7VT6OxKE9nhPKNy1ZlHbEdL/qPNRdHsy2i5WAIk41yt6emM1Jw5OcL12eg4VR1rWmRCGAVEAGjhncseLQ7SOebZV6t4TxWEBcFHHOayO7PoHUcpy/y5FSP+jWJ06KoKRDAUngZxDUXrVlOsdR0jX8vjsxaqI1dilG0ciWdjgHcN4GqgERdcR1/VayjwgBFDzQG/yJEaASKgSIYBnAgg8BKAARRWKvkcjDwMhgAKIjkcACREJlgOuAu5K7Qgwaz0KeQaOOn9vTCsBwLwApMiZdOBROpnj3u2bdX5aPQfwaOBuDIr1HveYryldl30wBO4RZ4AS1aXCbFr2skezTa5b2+7Z7Z2nNjZKh3Jja3HGHtlAcnwyOj+/HIIFUACd0TizbhzTQU1ddAzYg+WYY5d0TM5dXql6A2VzO7n0jUS6kTtXuseVQ4Im01icXxgfApgOuvy8pevPjD3VuRrhRpu29trG2GI7eWPFkKCJerZdzH2UV8NUAsiqR8pnG5YdJjjNZA+8aYvqIq6dQqNfC9N5YofvDNuO3HbQ/tNG+dwVQOmwS8QAg/kY26lABF17Aqq65DgSlSkzMR2ZpM53hmsve32mvehLIvIcoFyIaUB9vhZB3bBmbJ6cLJkfrb4PRj+FmSAeCsbxiXF+0mSAG5WhDtjSfT32S3TtUb6wsMS6Qkd4TtKQoI62b0FTPkW3EQQ6G908KgcB5aA9BKir4WFf59q4ZNneF0eYZ/4SUxtB4lg88rVoLnqEa7CAOqz9J0Y9Caw/N44FnX0zYQPHRXsI4RqQ0Zk/B1iNUB5RYKohgN4svNG8GiApCpbr2QN2uHN08nmBGSK4ttyLkd1D1TBgxGUV4F1OOk/PSWUTENsRqJ3ovo1M5gHo+4A13/YPRd4sS2c7V/m1795WMJvZlNvIUv1RTtzXw2mqncXbnsSSqCJwIuCua3GetX4zBBj+ntDlBCeCYGNg2Vo7UXIeR6nv9fSPE0EIoAdwfs095xis85NeAC/SyWDSCQLXJ6cT9T2Ajo+MQOhkgWTMBMh55iecPSxDQHBLEwCG7WfJkd13CYS6mvpXrJPAoKURQdILATnSkrKbjTJLugc7piGAQWZEkDS7dMIj5utWulQf2SNIzk8hgD6zIoBQRy36n0U0k/sQgAIogjT/Z9saHO1pV3YhACUuImCcNWRPsGVZ1ocAjEAh9JwmOAsyaG9kPycEMAAUQWIcp3CWt71gB84SP34JJ7Gzm0I1A/a0hGomsTigF6VWZ3pEfQjAAY3jLM7yticRAnAEOW7GUd72ECEAZ2qneTjL294NKaX/gKttC9Kft4MAAAAASUVORK5CYII="};let s=window._p5play_intro_image;""==s||s?.includes("made_with_p5play")?((s.includes("bit.")||s.includes("pixel"))&&(i.style.imageRendering="pixelated"),i.src=s):i.src="https://p5play.org/assets/made_with_p5play.webp",await new Promise((t=>i.onload=t)),e.append(i),document.body.append(e),await t.delay(),i.offsetHeight,i.style.scale=1.2,await t.delay(1100),i.style.opacity=0,await t.delay(400),e.style.display="none",e.remove(),document.getElementById("p5play-intro")?.remove(),r||t._decrementPreload()}()}}const v=t.createCanvas;this.createCanvas=function(){let e,s,r,o=[...arguments];if("string"==typeof o[0])if(o[0].includes(":")){let t=o[0].split(":"),i=Number(t[0]),s=Number(t[1]),r=window.innerWidth,h=window.innerWidth*(s/i);h>window.innerHeight&&(r=window.innerHeight*(i/s),h=window.innerHeight),o[0]=Math.round(r),o.splice(1,0,Math.round(h)),e="fullscreen"}else o=[0,0,...o];if(o[0]||(o[0]=window.innerWidth,o[1]=window.innerHeight,e="fullscreen"),"string"==typeof o[2]){let t=o[2].toLowerCase().split(" ");"pixelated"==t[0]?(s="pixelated",t[1]?(e="centered",r=Number(t[1].slice(1))):e="fullscreen",o.splice(2,1)):"fullscreen"==t[0]&&(e="fullscreen",o.splice(2,1))}let h=v.call(t,...o);t.ctx=t.drawingContext;let a=h.canvas||h;window.canvas=a,h.GL?(a.renderer="webgl",t._webgl=!0):t._webgpu||(t._c2d=!0),a.tabIndex=0,a.w=o[0],a.h=o[1],a.addEventListener&&(a.addEventListener("keydown",(function(t){" "!=t.key&&"/"!=t.key&&"ArrowUp"!=t.key&&"ArrowDown"!=t.key&&"ArrowLeft"!=t.key&&"ArrowRight"!=t.key||t.preventDefault()})),a.addEventListener("mouseover",(()=>{this.mouse.isOnCanvas=!0,this.mouse.isActive=!0})),a.addEventListener("mouseleave",(()=>{this.mouse.isOnCanvas=!1})),a.addEventListener("touchstart",(t=>t.preventDefault())),a.addEventListener("contextmenu",(t=>t.preventDefault()))),a.save??=t.saveCanvas.bind(t),a.resize??=t.resizeCanvas.bind(t),a.hw=.5*a.w,a.hh=.5*a.h,a.mouse={x:t.mouseX,y:t.mouseY},t._c2d?(t.camera.x=t.camera.ogX=a.hw,t.camera.y=t.camera.ogY=a.hh):(t.camera.x=0,t.camera.y=0,t._webgpu&&(t.p5play._renderStats={x:10-a.hw,y:20-a.hh})),p5.disableFriendlyErrors=i,t.displayMode(e,s,r);let n=window.PointerEvent?"pointer":"mouse";return a.addEventListener(n+"down",z),window&&(window.addEventListener(n+"move",j),window.addEventListener(n+"up",P)),h},this.Canvas=class{constructor(t,e,i){this.w,this.width,this.h,this.height,this.hw,this.hh,this.mouse}resize(){}save(){}},t.Canvas=function(){return t.createCanvas(...arguments).canvas};const x=t.resizeCanvas;this.resizeCanvas=(e,i)=>{e??=window.innerWidth,i??=window.innerHeight,x.call(t,e,i);let s=t.canvas;s.w=s.width/t.pixelDensity(),s.h=s.height/t.pixelDensity(),s.hw=.5*s.w,s.hh=.5*s.h,s.fullscreen&&(s.w/s.h>window.innerWidth/window.innerHeight?(s.style.width="100%!important",s.style.height="auto!important"):(s.style.width="auto!important",s.style.height="100%!important")),t._c2d?(t.camera.x=s.hw,t.camera.y=s.hh):(t.camera.x=0,t.camera.y=0)};const b=t.frameRate;this.frameRate=function(e){let i=b.call(t,e);return e&&t.world._setTimeStep(),i};const S=t.background;this.background=function(){let e=arguments;1==e.length&&1==e[0]?.length?S.call(t,t.colorPal(e[0])):S.call(t,...e)};const A=t.fill;this.fill=function(){let e=arguments;1==e.length&&1==e[0]?.length?A.call(t,t.colorPal(e[0])):A.call(t,...e)};const C=t.stroke;if(this.stroke=function(){let e=arguments;1==e.length&&1==e[0]?.length?C.call(t,t.colorPal(e[0])):C.call(t,...e)},!r){const e=t.loadImage;this.loadImage=this.loadImg=function(){if(t.p5play.disableImages)return t._q5||t._decrementPreload(),{w:16,width:16,h:16,height:16,pixels:[]};let i,s=arguments,r=s[0],o=t.p5play.images[r];if("function"==typeof s[s.length-1]&&(i=s[s.length-1]),o)return o.width<=1&&o.height<=1?i?(o.cbs.push(i),o.calls++):t._q5||t._decrementPreload():(i&&i(),t._q5||t._decrementPreload()),o;return o=e.call(t,r,(e=>{if(e.w||(Object.defineProperty(e,"w",{get:function(){return this.width}}),Object.defineProperty(e,"h",{get:function(){return this.height}})),e.cbs){for(let t of e.cbs)t(e);if(!t._q5)for(let i=1;i\nhtml, body {\n\tmargin: 0;\n\tpadding: 0;\n}\nbody.hasFrameBorder {\n\tdisplay: block;\n}\n.p5Canvas {\n\toutline: none;\n\t-webkit-touch-callout: none;\n\t-webkit-text-size-adjust: none;\n\t-webkit-user-select: none;\n\toverscroll-behavior: none;\n}\n.p5-pixelated {\n\timage-rendering: pixelated;\n\tfont-smooth: never;\n\t-webkit-font-smoothing: none;\n}\n.p5-centered,\n.p5-maxed,\n.p5-fullscreen {\n display: flex;\n\talign-items: center;\n\tjustify-content: center;\n}\nmain.p5-centered,\nmain.p5-maxed,\n.p5-fullscreen {\n\theight: 100vh;\n}\nmain {\n\toverscroll-behavior: none;\n}\n"),t._adjustDisplay=()=>{let e=t.canvas,i=e.style,s=e.parentElement;i&&s&&e.displayMode&&("pixelated"==e.renderQuality&&(e.classList.add("p5-pixelated"),t.pixelDensity(1),t.noSmooth&&t.noSmooth(),t.textFont&&t.textFont("monospace")),"normal"==e.displayMode?(s.classList.remove("p5-centered","p5-maxed","p5-fullscreen"),i.width=e.w*e.displayScale+"px",i.height=e.h*e.displayScale+"px"):(s.classList.add("p5-"+e.displayMode),s=s.getBoundingClientRect(),e.w/e.h>s.width/s.height?("centered"==e.displayMode?(i.width=e.w*e.displayScale+"px",i.maxWidth="100%"):i.width="100%",i.height="auto",i.maxHeight=""):(i.width="auto",i.maxWidth="","centered"==e.displayMode?(i.height=e.h*e.displayScale+"px",i.maxHeight="100%"):i.height="100%")))},t.displayMode=(e="normal",i="default",s=1)=>{let r=t.canvas;"string"==typeof s&&(s=parseFloat(s.slice(1))),Object.assign(r,{displayMode:e,renderQuality:i,displayScale:s}),t._adjustDisplay()});let k={generic:["Ah! I found an error","Oh no! Something went wrong","Oof! Something went wrong","Houston, we have a problem","Whoops, having trouble here"],Sprite:{constructor:{base:"Sorry I'm unable to make a new Sprite",0:"What is $0 for? If you're trying to specify the x position of the sprite, please specify the y position as well.",1:"If you're trying to specify points for a chain Sprite, please use an array of position arrays.\n$0",2:"Invalid input parameters: $0"},hw:{0:"I can't change the halfWidth of a Sprite directly, change the sprite's width instead."},hh:{1:"I can't change the halfHeight of a Sprite directly, change the sprite's height instead."},rotate:{0:"Can't use this function on a sprite with a static collider, try changing the sprite's collider type to kinematic.",1:'Can\'t use "$0" for the angle of rotation, it must be a number.'},rotateTo:{},rotateMinTo:{},rotateTowards:{},changeAnimation:'I can\'t find any animation named "$0".',collide:{0:"I can't make that sprite collide with $0. Sprites can only collide with another sprite or a group.",1:"The collision callback has to be a function.",2:"You're trying to check for an collision with a sprite or group that doesn't exist!"},overlap:{0:"I can't make that sprite overlap with $0. Sprites can only overlap with another sprite or a group.",1:"The overlap callback has to be a function.",2:"You're trying to check for an overlap with a sprite or group that doesn't exist!"}},Ani:{constructor:{base:"Hey so, I tried to make a new Ani but couldn't",1:"The name of the animation must be the first input parameter."},frame:"Index $0 out of bounds. That means there is no frame $0 in this animation. It only has $1 frames!"},Group:{constructor:{base:"Hmm awkward! Well it seems I can't make that new Group you wanted"}}};k.Group.collide=k.Sprite.collide,k.Group.overlap=k.Sprite.overlap,k.Sprite.rotateTo[0]=k.Sprite.rotateMinTo[0]=k.Sprite.rotateTowards[0]=k.Sprite.rotate[0];class M extends Error{constructor(t,e,i){super(),"string"!=typeof t&&(i=e,e=t,t=(t=this.stack.match(/\n\s*at ([^\(]*)/)[1]).slice(0,-1)),"number"!=typeof e&&(i=e,e=void 0),"new"==t.slice(0,3)&&(t=t.slice(4));let s=(t=t.split("."))[0];t=t[1]||"constructor";let r=this.stack.match(/\/([^p\/][^5][^\/:]*:[^\/:]+):/);r&&(r=r[1].split(":"),r=" in "+r[0]+" at line "+r[1]),r=" using "+s+"."+t+". ",i=i||[];let o,h=k[s][t];o=h.base?h.base+r:k.generic[Math.floor(Math.random()*k.generic.length)]+r,void 0!==e&&(h=h[e]),h&&(h=h.replace(/\$([0-9]+)/g,((t,e)=>i[e])),o+=h),p5._friendlyError(o,t)}}this.allSprites=new t.Group,this.world=new t.World,this.camera=new t.Camera,this.InputDevice=class{constructor(){this.holdThreshold=12,this._default=0}_ac(t){return t}presses(t){return t??=this._default,void 0===this[t]&&(t=this._ac(t)),1==this[t]||-3==this[t]}pressing(t){return t??=this._default,void 0===this[t]&&(t=this._ac(t)),-3==this[t]?1:this[t]>0?this[t]:0}pressed(t){return this.released(t)}holds(t){return t??=this._default,void 0===this[t]&&(t=this._ac(t)),this[t]==this.holdThreshold}holding(t){return t??=this._default,void 0===this[t]&&(t=this._ac(t)),this[t]>=this.holdThreshold?this[t]:0}held(t){return t??=this._default,void 0===this[t]&&(t=this._ac(t)),-2==this[t]}released(t){return t??=this._default,void 0===this[t]&&(t=this._ac(t)),this[t]<=-1}releases(t){return this.released(t)}},this._Mouse=class extends t.InputDevice{constructor(){super(),this._default="left";let e=this;this._pos=t.createVector.call(t),Object.defineProperty(this._pos,"x",{get:()=>e.x,set(t){e.x=t}}),Object.defineProperty(this._pos,"y",{get:()=>e.y,set(t){e.y=t}}),this.x=0,this.y=0,this.canvasPos={},this.left=0,this.center=0,this.right=0,this.drag={left:0,center:0,right:0},this._dragFrame={left:!1,center:!1,right:!1},this.isOnCanvas=!1,this.isActive=!1,this._visible=!0,this._cursor="default",this._ogX=0,this._ogY=0}_ac(t){return"left"==(t=t.toLowerCase()).slice(0,4)?t="left":"right"==t.slice(0,5)?t="right":"middle"==t.slice(0,6)&&(t="center"),t}_update(){t.mouse.canvasPos.x=t.mouseX,t.mouse.canvasPos.y=t.mouseY,t.camera.x==t.camera.ogX&&t.camera.y==t.camera.ogY&&1==t.camera.zoom?(this.x=t.mouseX,this.y=t.mouseY):t._webgpu?(this.x=t.mouseX/t.camera.zoom+t.camera.x,this.y=t.mouseY/t.camera.zoom+t.camera.y):(this.x=(t.mouseX-t.canvas.hw)/t.camera.zoom+t.camera.x,this.y=(t.mouseY-t.canvas.hh)/t.camera.zoom+t.camera.y)}get pos(){return this._pos}get position(){return this._pos}get cursor(){return t.canvas.style.cursor}set cursor(e){e!=this._cursor&&(t.cursor(e),this._cursor=e)}get visible(){return this._visible}set visible(e){this._visible=e,t.canvas.style.cursor=e?"default":"none"}drags(t){return t??=this._default,1==this.drag[t]}dragging(t){return t??=this._default,this.drag[t]>0?this.drag[t]:0}dragged(t){return t??=this._default,this.drag[t]<=-1}},this.mouse=new t._Mouse,this._SpriteMouse=class extends t._Mouse{constructor(){super(),delete this.canvasPos,this.hover=0}hovers(){return 1==this.hover}hovering(){return this.hover>0?this.hover:0}hovered(){return this.hover<=-1}};let T=0,z=function(e){if(!t._setupDone)return;T++,!t._isQ5&&t.userStartAudio&&t.userStartAudio();let i="left";if(1===e.button?i="center":2===e.button&&(i="right"),t.mouse.isActive=!0,t.mouse[i]++,t.world.mouseSprites.length){let e=t.world.mouseSprite?.mouse;e&&(e[i]=0,e.hover=0,e.drag[i]=0);let s=t.world.mouseSprites[0];t.world.mouseSprite=s,e=s.mouse,e[i]=1,e.hover<=0&&(e.hover=1)}},j=function(e){if(!t._setupDone)return;let i="left";1===e.button?i="center":2===e.button&&(i="right");let s=t.mouse;s[i]>0&&(s._dragFrame[i]=!0)},P=function(e){if(!t._setupDone)return;if(!(T>0))return;T--;let i="left";1===e.button?i="center":2===e.button&&(i="right");let s=t.mouse;s[i]>=s.holdThreshold?s[i]=-2:s[i]>1?s[i]=-1:s[i]=-3,s.drag[i]>0&&(s.drag[i]=-1);let r=t.world.mouseSprite?.mouse;r&&(r.hover>1?(r[i]>=t.mouse.holdThreshold?r[i]=-2:r[i]>1?r[i]=-1:r[i]=-3,r.drag[i]>0&&(r.drag[i]=-1)):(r[i]=0,r.drag[i]=0))};if(this._Touch=class extends t.InputDevice{constructor(e){super(),this.x,this.y,this.id=e.identifier,this._default="duration",this.holdThreshold=t.touches.holdThreshold,this.duration=1,this.drag=0,this._dragFrame=!1,this.canvasPos={},this._update(e)}_update(e){let i=t.canvas;const s=i.getBoundingClientRect(),r=i.scrollWidth/i.w||1,o=i.scrollHeight/i.h||1,h=this.canvasPos.x=(e.clientX-s.left)/r,a=this.canvasPos.y=(e.clientY-s.top)/o;t.camera.x==i.hw&&t.camera.y==i.hh&&1==t.camera.zoom?(this.x=h,this.y=a):(this.x=(h-i.hw)/t.camera.zoom+t.camera.x,this.y=(a-i.hh)/t.camera.zoom+t.camera.y),this.force=e.force}},t.touches=[],t.touches.holdThreshold=12,t._ontouchstart=function(e){if(t._setupDone){t.getAudioContext&&"suspended"==t.getAudioContext()?.state&&t.userStartAudio();for(let i of e.changedTouches)t.touches.push(new t._Touch(i)),1==t.touches.length&&(t.mouseX=t.touches[0].x,t.mouseY=t.touches[0].y,t.mouse._update(),t.world.mouseSprites=t.world.getMouseSprites(),s&&t._onmousedown(e));t.touchStarted&&!t.touchStarted(e)&&e.preventDefault()}},t._ontouchmove=function(e){if(t._setupDone){for(let i of e.changedTouches){let r=t.touches.find((t=>t.id==i.identifier));r._update(i),r._dragFrame=!0,r.id==t.touches[0].id&&(t.mouseX=t.touches[0].x,t.mouseY=t.touches[0].y,t.mouse._update(),s&&t._onmousemove(e))}t.touchMoved&&!t.touchMoved(e)&&e.preventDefault()}},t._ontouchend=function(e){if(t._setupDone){for(let i of e.changedTouches){let r=t.touches.find((t=>t.id==i.identifier));r._update(i),r.duration>=r.holdThreshold?r.duration=-2:r.duration>1?r.duration=-1:r.duration=-3,r.drag>0&&(r.drag=-1),r.id==t.touches[0].id&&(t.mouseX=t.touches[0].x,t.mouseY=t.touches[0].y,t.mouse._update(),s&&t._onmouseup(e))}t.touchEnded&&!t.touchEnded(e)&&e.preventDefault()}},this._Keyboard=class extends t.InputDevice{constructor(){super(),this._default=" ",this.alt=0,this.arrowUp=0,this.arrowDown=0,this.arrowLeft=0,this.arrowRight=0,this.backspace=0,this.capsLock=0,this.control=0,this.enter=0,this.meta=0,this.shift=0,this.tab=0;let t=this._simpleKeyControls={arrowUp:"up",arrowDown:"down",arrowLeft:"left",arrowRight:"right"};t.w=t.W="up",t.s=t.S="down",t.a=t.A="left",t.d=t.D="right",t.i=t.I="up2",t.k=t.K="down2",t.j=t.J="left2",t.l=t.L="right2"}get visible(){return this._inp==document.activeElement}set visible(t){this._inp||(this._inp=Object.assign(document.createElement("input"),{type:"text",style:"position: fixed; height: 0; padding: 0; border: none; opacity: 0.0001; pointer-events: none;"}),document.body.appendChild(this._inp)),this._visible=t,t?this._inp.focus():this._inp.blur()}_ac(t){if(1!=t.length){if(!isNaN(t)){if(38==t)return"arrowUp";if(40==t)return"arrowDown";if(37==t)return"arrowLeft";if(39==t)return"arrowRight";if(t>=10)throw new Error("Use key names with the keyboard input functions, not keyCode numbers!");return t}t=t.replaceAll(/[ _-]/g,"")}if(1!=(t=t.toLowerCase()).length){if("arrowup"==t)return"arrowUp";if("arrowdown"==t)return"arrowDown";if("arrowleft"==t)return"arrowLeft";if("arrowright"==t)return"arrowRight";if("capslock"==t)return"capsLock"}return t}_pre(t){(!this[t]||this[t]<0)&&(this[t]=1)}_rel(t){this[t]>=this.holdThreshold?this[t]=-2:this[t]>1?this[t]=-1:this[t]=-3}get cmd(){return this.meta}get command(){return this.meta}get ctrl(){return this.control}get space(){return this[" "]}get spacebar(){return this[" "]}get opt(){return this.alt}get option(){return this.alt}get win(){return this.meta}get windows(){return this.meta}},this.kb=new t._Keyboard,this.keyboard=t.kb,"object"==typeof navigator&&navigator.keyboard){const e=navigator.keyboard;window==window.top?e.getLayoutMap().then((e=>{"w"!=e.get("KeyW")&&(t.p5play.standardizeKeyboard=!0)})):t.p5play.standardizeKeyboard=!0}else t.p5play.standardizeKeyboard=!0;function O(t){let e=t.code;return 4==e.length&&"Key"==e.slice(0,3)?e[3].toLowerCase():t.key}let F=function(t){let e=t.key;if(this.p5play.standardizeKeyboard&&(e=O(t)),e.length>1)e=e[0].toLowerCase()+e.slice(1);else{let t=e.toLowerCase(),i=e.toUpperCase();t!=i&&(e!=i?this.kb._pre(i):this.kb._pre(t))}this.kb._pre(e);let i=this.kb._simpleKeyControls[e];i&&this.kb._pre(i)},D=function(t){let e=t.key;if(this.p5play.standardizeKeyboard&&(e=O(t)),e.length>1)e=e[0].toLowerCase()+e.slice(1);else{let t=e.toLowerCase(),i=e.toUpperCase();t!=i&&(e!=i?this.kb._rel(i):this.kb._rel(t))}this.kb._rel(e);let i=this.kb._simpleKeyControls[e];if(i&&this.kb._rel(i),t.shiftKey){let t=e.toLowerCase();this.kb[t]>0&&this.kb._rel(t)}};window&&(window.addEventListener("keydown",F.bind(this)),window.addEventListener("keyup",D.bind(this))),this.Contro=class extends t.InputDevice{constructor(t){super(),this._default="a",this.connected=!0,this.a=0,this.b=0,this.x=0,this.y=0,this.l=0,this.r=0,this.lt=0,this.rt=0,this.select=0,this.start=0,this.lsb=0,this.rsb=0,this.up=0,this.down=0,this.left=0,this.right=0,this.leftStick={x:0,y:0},this.rightStick={x:0,y:0},this.leftTrigger=0,this.rightTrigger=0,this.buttonMapping={a:0,b:1,x:2,y:3,l:4,r:5,lt:6,rt:7,select:8,start:9,lsb:10,rsb:11,up:12,down:13,left:14,right:15},this.axeMapping={leftStick:{x:0,y:1},rightStick:{x:2,y:3},leftTrigger:4,rightTrigger:5},this.isMock=!1,"string"!=typeof t?(this.gamepad=t,this.id=t.id):(this.gamepad={},this.id=t,this.isMock=!0),this._axeTriggers=this.gamepad.axes&&void 0!==this.gamepad.axes[this.axeMapping.leftTrigger],this.hasAnalogTriggers=this._axeTriggers||void 0,this.id.includes("GuliKit")&&(this.buttonMapping.a=1,this.buttonMapping.b=0,this.buttonMapping.x=3,this.buttonMapping.y=2)}_ac(t){return"lb"==(t=t.toLowerCase())?t="l":"rb"==t?t="r":"leftstickbutton"==t?t="lsb":"rightstickbutton"==t&&(t="rsb"),t}_update(){if(this.isMock)return;if(this.gamepad=navigator.getGamepads()[this.gamepad.index],!this.gamepad?.connected)return;let t=this.gamepad;for(let e in this.buttonMapping){let i=this.buttonMapping[e],s=t.buttons[i];s&&(s.pressed?this[e]++:this[e]=this[e]>0?-1:0)}return this.leftStick.x=t.axes[this.axeMapping.leftStick.x],this.leftStick.y=t.axes[this.axeMapping.leftStick.y],this.rightStick.x=t.axes[this.axeMapping.rightStick.x],this.rightStick.y=t.axes[this.axeMapping.rightStick.y],this._axeTriggers?(this.leftTrigger=t.axes[this.axeMapping.leftTrigger],this.rightTrigger=t.axes[this.axeMapping.rightTrigger]):(this.leftTrigger=t.buttons[this.buttonMapping.lt].value,this.rightTrigger=t.buttons[this.buttonMapping.rt].value,void 0===this.hasAnalogTriggers&&(this.leftTrigger||this.rightTrigger)&&(this.hasAnalogTriggers=!Number.isInteger(this.leftTrigger)||!Number.isInteger(this.rightTrigger))),!0}_reset(){for(let t in this.buttonMapping)this[t]=0;this.leftStick.x=0,this.leftStick.y=0,this.rightStick.x=0,this.rightStick.y=0,this.leftTrigger=0,this.rightTrigger=0}get cross(){return this.a}get circle(){return this.b}get square(){return this.x}get triangle(){return this.y}get ls(){return this.leftStick}get rs(){return this.rightStick}get lb(){return this.l}get rb(){return this.r}get l1(){return this.l}get r1(){return this.r}get zl(){return this.lt}get zr(){return this.rt}get l2(){return this.leftTrigger}get r2(){return this.rightTrigger}get leftStickButton(){return this.lsb}get rightStickButton(){return this.rsb}get l3(){return this.lsb}get r3(){return this.rsb}},this._Contros=class extends Array{constructor(){if(super(),window&&(window.addEventListener("gamepadconnected",(t=>{this._onConnect(t.gamepad)})),window.addEventListener("gamepaddisconnected",(t=>{this._onDisconnect(t.gamepad)}))),"object"!=typeof navigator||!navigator.getGamepads)return;let t=navigator.getGamepads();for(let e of t)e&&this._onConnect(e)}swap(e,i){let s=this[e];this[e]=this[i],this[i]=s,0!=e&&0!=i||(t.contro=this[0],!t._q5&&t._isGlobal&&(window.contro=this[0]))}remove(t){this[t]=null}onConnect(t){return!0}onDisconnect(t){return!1}_onConnect(e){if(e){for(let t=0;tt.p5play._fps,t.renderStats=()=>{let e=t.p5play._renderStats;if(e.fontSize||(1==t.allSprites.tileSize||t.allSprites.tileSize>16?e.fontSize=16:e.fontSize=10,e.gap=1.25*e.fontSize),!t.p5play._fpsAvg||t.frameCount%20==0){let e=0,i=t.p5play._fpsArr.length;for(let s=0;s55?t.color(30,255,30):r>25?t.color(255,100,30):t.color(255,30,30),t.p5play._statsColor=s}t.p5play._fpsArr.push(t.getFPS()),t.push(),t.fill(0,0,0,128),t.rect(e.x-5,e.y-e.fontSize,8.5*e.fontSize,4*e.gap+5),t.fill(t.p5play._statsColor),t.textAlign("left"),t.textSize(e.fontSize),e.font&&t.textFont(e.font);let i=e.x,s=e.y;t.text("sprites: "+t.p5play.spritesDrawn,i,s),t.text("fps avg: "+t.p5play._fpsAvg,i,s+e.gap),t.text("fps min: "+t.p5play._fpsMin,i,s+2*e.gap),t.text("fps max: "+t.p5play._fpsMax,i,s+3*e.gap),t.pop()}},p5playAfterSetup=function(){const t=this;t._isGlobal&&window.update&&(t.update=window.update,t._q5||(window.draw=()=>{})),t._isGlobal&&window.drawFrame&&(t.drawFrame=window.drawFrame),this.update??=()=>{},this.drawFrame??=()=>{},t._setupDone=!0},p5playPreDraw=function(){const t=this;t._q5||(t.p5play._preDrawFrameTime=performance.now()),t.p5play.spritesDrawn=0,t.mouse._update(),t.contros._update(),t.update(),t.allSprites._autoUpdate&&t.allSprites.update(),t.allSprites._autoUpdate??=!0},p5playPostDraw=function(){const t=this;t.p5play._inPostDraw=!0,t.allSprites.autoCull&&t.allSprites.cull(1e4),t.world.autoStep&&t.world.timeScale>0&&t.world.physicsUpdate(),t.world.autoStep??=!0,t.drawFrame(),t.allSprites._autoDraw&&(t.camera.on(),t.allSprites.draw()),t.allSprites._autoDraw??=!0,t.camera.off(),t.allSprites.postDraw(),t.p5play.renderStats&&t.renderStats();for(let e in t.kb)"holdThreshold"!=e&&(t.kb[e]<0?t.kb[e]=0:t.kb[e]>0&&t.kb[e]++);for(let e=0;e0&&e[t]++,i?.hover&&(i[t]=e[t]),e._dragFrame[t]?(e.drag[t]++,i&&(i.drag[t]=e.drag[t]),e._dragFrame[t]=!1):e.drag[t]<0&&(e.drag[t]=0,i&&(i.drag[t]=0));if(t.world.mouseTracking&&t.mouse.isActive){let s=t.world.getMouseSprites();for(let t=0;t0?e.mouse.hover=-1:e.mouse.hover<0&&(e.mouse.hover=0)}e.left<=0&&e.center<=0&&e.right<=0&&(t.world.mouseSprite=null);let r=t.world.mouseSprite,o=e.drag.left>0||e.drag.center>0||e.drag.right>0;for(let e of t.world.mouseSprites)if(!s.includes(e)){let i=e.mouse;i.hover>0&&(i.hover=-1,i.left=i.center=i.right=0),o||e!=r||(t.world.mouseSprite=r=null)}r&&(s.includes(r)||s.push(r),i.x=r.x-e.x,i.y=r.y-e.y),t.world.mouseSprites=s}t._q5||(t.p5play._postDrawFrameTime=performance.now(),t.p5play._fps=Math.round(1e3/(t.p5play._postDrawFrameTime-t.p5play._preDrawFrameTime))||1),t.p5play._inPostDraw=!1};null==p5.prototype?.registerMethod?(console.error("p5play is not compatible with p5.js v2. Please use p5.js v1 or q5.js. https://q5js.org"),p5.registerAddon(((t,e,i)=>{let s={loadImage:()=>new t.Image(1,1),loadModel:()=>new t.Geometry,loadJSON:()=>{},loadStrings:()=>[],loadFont:e=>new t.Font(e,new FontFace("default","default.woff"))};t.isPreloadSupported=()=>!0;let r=[],o={};for(let t in s){let i=e[t];o[t]=i,e[t]=function(...e){if(!this._isInPreload)return i.apply(this,e);let o=s[t](this),h=i.apply(this,e).then((t=>{for(let e in t)o[e]=t[e]}));return r.push(h),o}}delete e.code,i.presetup=async function(){const t=this;if(t.createCanvas.call(t,100,100),p5playInit.call(t),t._isGlobal){let e=["p5play","DYN","DYNAMIC","STA","STATIC","KIN","KINEMATIC","Sprite","Ani","Anis","Group","World","world","createCanvas","Canvas","canvas","displayMode","Camera","camera","Tiles","Joint","GlueJoint","DistanceJoint","WheelJoint","HingeJoint","SliderJoint","RopeJoint","GrabberJoint","kb","keyboard","mouse","touches","allSprites","camera","contro","contros","controllers","spriteArt","EmojiImage","getFPS"];for(let i of e)window[i]=t[i]}window.preload&&(t._isInPreload=!0,window.preload(),t._isInPreload=!1,await Promise.all(r))},i.postsetup=p5playAfterSetup,i.predraw=p5playPreDraw,i.postdraw=p5playPostDraw}))):(p5.prototype.registerMethod("init",p5playInit),p5.prototype.registerMethod("afterSetup",p5playAfterSetup),p5.prototype.registerMethod("pre",p5playPreDraw),p5.prototype.registerMethod("post",p5playPostDraw)); 2 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "p5play", 3 | "author": "quinton-ashley ", 4 | "bugs": { 5 | "url": "https://github.com/quinton-ashley/p5play/issues" 6 | }, 7 | "contributors": [ 8 | "Paolo Pedercini " 9 | ], 10 | "description": "A JavaScript game engine that uses p5.js for graphics and Box2D for physics.", 11 | "funding": [ 12 | { 13 | "type": "patreon", 14 | "url": "https://www.patreon.com/q5play" 15 | }, 16 | { 17 | "type": "ko-fi", 18 | "url": "https://ko-fi.com/q5play" 19 | }, 20 | { 21 | "type": "github", 22 | "url": "https://github.com/sponsors/quinton-ashley" 23 | } 24 | ], 25 | "keywords": [ 26 | "p5.play", 27 | "p5.js", 28 | "p5js", 29 | "p5", 30 | "q5", 31 | "box2d", 32 | "physics", 33 | "physics engine", 34 | "physics simulation", 35 | "game engine", 36 | "games", 37 | "animation", 38 | "user input", 39 | "controller", 40 | "gamepad" 41 | ], 42 | "license": "p5play Personal License", 43 | "main": "p5play.js", 44 | "types": "p5play.d.ts", 45 | "homepage": "https://p5play.org", 46 | "repository": { 47 | "type": "git", 48 | "url": "git+https://github.com/quinton-ashley/p5play.git" 49 | }, 50 | "scripts": { 51 | "min": "terser p5play.js --compress ecma=2025 --mangle > p5play.min.js", 52 | "v": "npm version patch --force", 53 | "V": "npm version minor --force", 54 | "version": "git add -A", 55 | "postversion": "git push" 56 | }, 57 | "version": "3.30.2" 58 | } 59 | --------------------------------------------------------------------------------