diff --git a/src/channels/dvd-ss/index.tsx b/src/channels/dvd-ss/index.tsx new file mode 100644 index 0000000..bacd9fe --- /dev/null +++ b/src/channels/dvd-ss/index.tsx @@ -0,0 +1,160 @@ +import type { FormattedDonation, Total } from '@gdq/types/tracker'; +import { ChannelProps, registerChannel } from '../channels'; + +import { useEffect, useRef } from 'react'; +import { useListenFor, useReplicant } from 'use-nodecg'; +import styled from '@emotion/styled'; +import TweenNumber from '@gdq/lib/components/TweenNumber'; + +import * as PIXI from 'pixi.js'; +import { usePIXICanvas } from '@gdq/lib/hooks/usePIXICanvas'; +import { CRTFilter } from '@pixi/filter-crt'; + +const SPEED_DECAY = 0.995; +const ACCELERATION = 10; + +registerChannel('DVD Screen Saver', 4, DVDSS, { + position: 'bottomLeft', + site: 'GitHub', + handle: 'wporter82', +}); + +function DVDSS(props: ChannelProps) { + const [total] = useReplicant('total', null); + const totalRef = useRef(null); + const containerRef = useRef(null); + const crtFilter = useRef(null); + const bg = useRef(null); + const totalSprite = useRef(null); + + var randX = randRange(0, 900); + var randY = randRange(10, 300); + + const pos = useRef({x:randX, y:randY}); + const vel = useRef({x:randVel(), y:randVel()}); + + var color = useRef(getRandomColor()); + + const frameCount = useRef(0); + + const [pixiApp, canvasRef] = usePIXICanvas({width: 1092, height: 332}, () =>{ + if (!canvasRef.current) return; + if (!containerRef.current) return; + if (!totalSprite.current) return; + + frameCount.current++; + + if (pos.current.y > 332 - totalSprite.current.height + 10) { + vel.current.y = -Math.abs(vel.current.y); + color.current = getRandomColor(); + } + if (pos.current.y < 0) { + vel.current.y = Math.abs(vel.current.y); + color.current = getRandomColor(); + } + if (pos.current.x > 1092 - totalSprite.current.width + 10) { + vel.current.x = -Math.abs(vel.current.x); + color.current = getRandomColor(); + } + if (pos.current.x < 0) { + vel.current.x = Math.abs(vel.current.x); + color.current = getRandomColor(); + } + + if (Math.abs(vel.current.x) > 1) + vel.current.x *= SPEED_DECAY; + if (Math.abs(vel.current.y) > 1) + vel.current.y *= SPEED_DECAY; + + if (Math.abs(vel.current.x) < 1) + vel.current.x = 1 * Math.sign(vel.current.x); + if (Math.abs(vel.current.y) < 1) + vel.current.y = 1 * Math.sign(vel.current.y); + + pos.current.x += vel.current.x; + pos.current.y += vel.current.y; + + if (totalSprite.current) { + totalSprite.current.x = pos.current.x; + totalSprite.current.y = pos.current.y; + totalSprite.current.style.fill = color.current; + + if (totalRef.current) { + totalSprite.current.text = totalRef.current.innerText; + } + } + }); + + useEffect(() => { + if (!pixiApp.current || crtFilter.current) return; + + crtFilter.current = new CRTFilter({ + vignetting: 0.25, + time: 0, + lineWidth: 1, + lineContrast: 0.5, + noise: 0.1, + }); + + totalSprite.current = new PIXI.Text("$0",{fontFamily: "monkeyisland", fontSize: "46px", fill: "#ff00ff"}); + + pixiApp.current.stage.filters = [crtFilter.current]; + bg.current = new PIXI.Graphics(); + + bg.current.beginFill(0x2a2a2a); + bg.current.drawRect(0, 0, 1092, 332); + pixiApp.current.stage.addChild(bg.current); + + pixiApp.current.stage.addChild(totalSprite.current); + + }, [pixiApp, crtFilter]); + + useListenFor('donation', (donation: FormattedDonation) => { + // Bounce around a little faster when 10k threshold is passed + const prevTotal = donation.rawNewTotal - donation.rawAmount; + if (prevTotal != 0) { + if (Math.floor(prevTotal / 10000) < Math.floor(donation.rawNewTotal / 10000)) { + vel.current.x *= ACCELERATION; + vel.current.y *= ACCELERATION; + } + } + }); + + return ( + + + + $ + + + ); +} + +const getRandomColor = (): string => { + return `hsl(${randRange(0,365)}, ${randRange(50,100)}%, ${randRange(50,80)}%)`; +} + +const randRange = (min: number, max: number): number => { + return Math.random() * (max - min) + min; +} + +const randVel = (): number => { + return (Math.random() < 0.5) ? 1 : -1; +} + +const Container = styled.div` + position: absolute; + width: 100%; + height: 100%; + padding: 0; + margin: 0; + background-color: #fafafa; +`; + +const PIXICanvas = styled.canvas` + position: absolute; +`; + +const TotalEl = styled.div` + display: none; +`; diff --git a/src/channels/index.ts b/src/channels/index.ts index 19cb3bb..8d12986 100644 --- a/src/channels/index.ts +++ b/src/channels/index.ts @@ -4,6 +4,7 @@ import './template'; import './breakout'; import './cave-story'; import './desert-bus'; +import './dvd-ss'; import './mega-man'; // import './mgs3'; import './monkey-island';