diff --git a/docs/README.md b/docs/README.md index e2920c2..97a2d95 100644 --- a/docs/README.md +++ b/docs/README.md @@ -278,6 +278,7 @@ title Balloon spawn chances "Confetti" : 0.10 "Fast": 0.10 "Gold": 0.05 + "Ghost": 0.10 ``` ## Inheritance Tree @@ -291,6 +292,7 @@ Default --|> Balloon Confetti --|> Default Fast --|> Default Gold --|> Default +Ghost --|> Default Splitter --|> Default ``` diff --git a/docs/balloons/ghost.md b/docs/balloons/ghost.md new file mode 100644 index 0000000..d853eb5 --- /dev/null +++ b/docs/balloons/ghost.md @@ -0,0 +1,21 @@ +# Ghost + +This is a balloon spawned from the spiritworld. Just like a ghost this balloon sways from side to side en turns slightly invisible. + +```mermaid +classDiagram +direction LR +class Balloon { <> } +click Balloon href "#abstract-balloon-class" "Abstract balloon class" + +class Ghost { + +spawn_chance: number$ + +<< get >>name: string + +<< get >>options: BalloonOptions +} +Ghost --|> Balloon +``` + +Has a custom image resource in [`/resources/balloons/ghost/balloon.svg`](/resources/balloons/ghost/balloon.svg). + +![Ghost balloon](/resources/balloons/ghost/balloon.svg) \ No newline at end of file diff --git a/resources/balloons/ghost/balloon.svg b/resources/balloons/ghost/balloon.svg new file mode 100644 index 0000000..e36ac91 --- /dev/null +++ b/resources/balloons/ghost/balloon.svg @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + diff --git a/resources/balloons/ghost/ghost.css b/resources/balloons/ghost/ghost.css new file mode 100644 index 0000000..8a8bcfb --- /dev/null +++ b/resources/balloons/ghost/ghost.css @@ -0,0 +1,17 @@ +@keyframes fade { + 0% { + opacity: 0.2; + } + + 100% { + opacity: 0.8; + } +} + +.balloon[data-balloon="ghost"] img { + animation-name: fade; + animation-duration: var(--animation-speed, 3000ms); + animation-iteration-count: infinite; + animation-direction: alternate; + animation-timing-function: ease-in-out; +} \ No newline at end of file diff --git a/src/balloons/fast.ts b/src/balloons/fast.ts index 43597cd..53c286e 100644 --- a/src/balloons/fast.ts +++ b/src/balloons/fast.ts @@ -12,7 +12,7 @@ export default class Fast extends Default { return { ...super.options, imageUrl: 'balloon.svg', - riseDuration: [5000, 7500], + riseDuration: [4000, 6500], size: [65, 75], }; } diff --git a/src/balloons/ghost.ts b/src/balloons/ghost.ts new file mode 100644 index 0000000..bf8c04e --- /dev/null +++ b/src/balloons/ghost.ts @@ -0,0 +1,36 @@ +import Default, { BalloonOptions } from './default'; +import { random } from '@/utils'; + +type GhostOptions = BalloonOptions & { + fadeSpeed: number | [number, number]; +}; + +export default class Ghost extends Default { + public static readonly spawn_chance: number = 0.1; + // @ts-ignore + public get name(): 'ghost' { + return 'ghost'; + } + + public get options(): GhostOptions { + return { + ...super.options, + imageUrl: 'balloon.svg', + riseDuration: [18000, 21000], + swingDuration: [3, 4], + swingOffset: 70, + fadeSpeed: [2000, 3000], + }; + } + + public build(): void { + super.build(); + this.importStylesheet('ghost.css'); + + const animationSpeed = + typeof this.options.fadeSpeed === 'number' + ? this.options.fadeSpeed + : random(this.options.fadeSpeed[0], this.options.fadeSpeed[1]); + this.element.style.setProperty('--animation-speed', `${animationSpeed}ms`); + } +} diff --git a/src/balloons/index.ts b/src/balloons/index.ts index 0f1a3a0..5b797d4 100644 --- a/src/balloons/index.ts +++ b/src/balloons/index.ts @@ -2,4 +2,5 @@ export { default as Default } from './default'; export { default as Confetti } from './confetti'; export { default as Gold } from './gold'; export { default as Splitter } from './splitter'; -export { default as Fast } from './fast'; \ No newline at end of file +export { default as Fast } from './fast'; +export { default as Ghost } from './ghost'; \ No newline at end of file diff --git a/src/popup/pages/Discovery.tsx b/src/popup/pages/Discovery.tsx index 22b37a6..97b7acf 100644 --- a/src/popup/pages/Discovery.tsx +++ b/src/popup/pages/Discovery.tsx @@ -104,7 +104,7 @@ export default () => { balloon: new balloon(), }; }), - ...new Array(1).fill({ + ...new Array(0).fill({ name: 'default', count: 0, balloon: new Balloons.Default(), diff --git a/tests/balloons/ghost.test.ts b/tests/balloons/ghost.test.ts new file mode 100644 index 0000000..58e7b23 --- /dev/null +++ b/tests/balloons/ghost.test.ts @@ -0,0 +1,23 @@ +import { Ghost } from '@/balloons'; +import fetchMock from 'jest-fetch-mock'; + +fetchMock.enableMocks(); + +describe('Ghost Balloon', () => { + let balloon: Ghost; + + beforeEach(() => { + balloon = new Ghost(); + + fetchMock.resetMocks(); + }); + + test('name should be "ghost"', () => { + expect(balloon.name).toBe('ghost'); + }); + + test('name should be the same as the class name', () => { + expect(balloon.name).toBe('ghost'); + expect(balloon.name).toBe(Ghost.name.toLowerCase()); + }); +});