diff --git a/resources/balloons/gold/balloon.svg b/resources/balloons/gold/balloon.svg new file mode 100644 index 00000000..aaf16a0c --- /dev/null +++ b/resources/balloons/gold/balloon.svg @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/balloons/confetti.ts b/src/balloons/confetti.ts index 0efc4771..a05d4ddf 100644 --- a/src/balloons/confetti.ts +++ b/src/balloons/confetti.ts @@ -5,7 +5,9 @@ import Default from './default'; export default class Confetti extends Default { public static readonly spawn_chance: number = 0.1; // @ts-ignore - public readonly name = 'confetti'; + public get name(): 'confetti' { + return 'confetti'; + } private readonly mask = document.createElement('img'); diff --git a/src/balloons/default.ts b/src/balloons/default.ts index e7b4a7e0..c7defa3f 100644 --- a/src/balloons/default.ts +++ b/src/balloons/default.ts @@ -31,6 +31,24 @@ export type BalloonOptions = { * If not provided, the default sound will be used. */ popSoundUrl: string; + /** + * The size of the balloon. + * + * The first value is the minimum size and the second value is the maximum size. + */ + size: [number, number]; + /** + * The duration thresholds for the rise animation. + * + * The first value is the minimum duration and the second value is the maximum duration. + */ + riseDurationThreshold: [number, number]; + /** + * The duration thresholds for the swing animation. + * + * The first value is the minimum duration and the second value is the maximum duration. + */ + swingDurationThreshold: [number, number]; /** * The amount of pixels the balloon should wave back and forth. * @@ -45,27 +63,23 @@ export type BalloonOptions = { export default class Default extends Balloon { public static readonly spawn_chance: number = 0.9; - public readonly name = 'default'; - public readonly options: BalloonOptions = { - dir_name: this.name, - imageUrl: '/icon.png', - popSoundUrl: '/pop.mp3', - swingOffset: 15, - waveDegrees: 8, - }; - /** - * The duration thresholds for the rise animation. - * - * The first value is the minimum duration and the second value is the maximum duration. - */ - public readonly riseDurationThreshold: [number, number] = [10000, 15000]; - /** - * The duration thresholds for the swing animation. - * - * The first value is the minimum duration and the second value is the maximum duration. - */ - public readonly swingDurationThreshold: [number, number] = [2, 4]; + public get name(): 'default' { + return 'default'; + } + + public get options(): BalloonOptions { + return { + dir_name: this.name, + imageUrl: this.originalPath('/icon.png'), + popSoundUrl: this.originalPath('/pop.mp3'), + size: [50, 75], + riseDurationThreshold: [10000, 15000], + swingDurationThreshold: [2, 4], + swingOffset: 15, + waveDegrees: 8, + }; + } /** * The image element for the balloon image. @@ -99,24 +113,37 @@ export default class Default extends Balloon { constructor() { super(); importStylesheet('default-styles', this.resourceLocation + 'default.css'); - // Load the pop sound - this.popSound.src = this.popSoundUrl; - // Load the balloon image - this.balloonImage.src = this.balloonImageUrl; + } + + /** + * Get the path for the resources of the default balloon. + * + * This should only be used in the balloon.options. + * + * @param path The path of the resource. + * @returns The original path of the resource. + */ + protected originalPath(path: string): string { + return '/../default' + path; } public build() { - const size = random(50, 75); const positionX = random(5, 95); + const size = random(this.options.size[0], this.options.size[1]); const riseDuration = random( - this.riseDurationThreshold[0], - this.riseDurationThreshold[1] + this.options.riseDurationThreshold[0], + this.options.riseDurationThreshold[1] ); const waveDuration = random( - this.swingDurationThreshold[0], - this.swingDurationThreshold[1] + this.options.swingDurationThreshold[0], + this.options.swingDurationThreshold[1] ); + // Load the pop sound + this.popSound.src = this.popSoundUrl; + // Load the balloon image + this.balloonImage.src = this.balloonImageUrl; + this.element.classList.add('balloon'); // Set css variables diff --git a/src/balloons/gold.ts b/src/balloons/gold.ts new file mode 100644 index 00000000..de632190 --- /dev/null +++ b/src/balloons/gold.ts @@ -0,0 +1,19 @@ +import Default, { BalloonOptions } from './default'; + +export default class Gold extends Default { + public static readonly spawn_chance: number = 0.05; + // @ts-ignore + public get name(): 'gold' { + return 'gold'; + } + + public get options(): BalloonOptions { + return { + ...super.options, + imageUrl: '/balloon.svg', + riseDurationThreshold: [15000, 20000], + swingDurationThreshold: [3, 4], + size: [100, 125], + }; + } +} diff --git a/src/balloons/index.ts b/src/balloons/index.ts index 05d885f6..872d0bc0 100644 --- a/src/balloons/index.ts +++ b/src/balloons/index.ts @@ -1,2 +1,3 @@ export { default as Default } from './default'; export { default as Confetti } from './confetti'; +export { default as Gold } from './gold'; diff --git a/tests/balloons/gold.test.ts b/tests/balloons/gold.test.ts new file mode 100644 index 00000000..d7c378fb --- /dev/null +++ b/tests/balloons/gold.test.ts @@ -0,0 +1,23 @@ +import { Gold } from '@/balloons'; +import fetchMock from 'jest-fetch-mock'; + +fetchMock.enableMocks(); + +describe('Gold Balloon', () => { + let balloon: Gold; + + beforeEach(() => { + balloon = new Gold(); + + fetchMock.resetMocks(); + }); + + test('name should be "gold"', () => { + expect(balloon.name).toBe('gold'); + }); + + test('name should be the same as the class name', () => { + expect(balloon.name).toBe('gold'); + expect(balloon.name).toBe(Gold.name.toLowerCase()); + }); +});